C++ 测试迭代器是否指向最后一项?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3516196/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:01:01  来源:igfitidea点击:

Testing whether an iterator points to the last item?

c++stliterator

提问by WilliamKF

I have an stl iterator resulting from a std::find() and wish to test whether it is the last element. One way to write this is as follows:

我有一个由 std::find() 产生的 stl 迭代器,并希望测试它是否是最后一个元素。一种写法如下:

mine *match = someValue;
vector<mine *> Mine(someContent);
vector<mine *>::iterator itr = std::find(Mine.begin(), Mine.end(), match);

if (itr == --Mine.end()) {
  doSomething;
}

But it seems to me that decrementing the end() iterator is asking for trouble, such as if the vector has no elements, then it would be undefined. Even if I know it will never be empty, it still seems ugly. I'm thinking that maybe rbegin() is the way to go, but am not certain as to best way to compare the forward iterator with a reverse iterator.

但在我看来,递减 end() 迭代器是在自找麻烦,例如如果向量没有元素,那么它将是未定义的。即使我知道它永远不会是空的,它仍然看起来很丑陋。我在想也许 rbegin() 是要走的路,但我不确定将前向迭代器与反向迭代器进行比较的最佳方式。

回答by GManNickG

Do this:

做这个:

// defined in boost/utility.hpp, by the way
template <typename Iter>
Iter next(Iter iter)
{
    return ++iter;
}

// first check we aren't going to kill ourselves
// then check if the iterator after itr is the end
if ((itr != Mine.end()) && (next(itr) == Mine.end()))
{
    // points at the last element
}

That is all. Never gives you undefined behavior, works on all iterators, good day.

就这些。永远不会给你未定义的行为,适用于所有迭代器,美好的一天。

Wrap it up for fun:

总结一下:

template <typename Iter, typename Cont>
bool is_last(Iter iter, const Cont& cont)
{
    return (iter != cont.end()) && (next(iter) == cont.end())
}

Giving:

给予:

if (is_last(itr, Mine))


If you're allergic to utility functions/nice looking code, do:

如果您对实用函数/漂亮的代码过敏,请执行以下操作:

if ((itr != Mine.end()) && (itr + 1 == Mine.end()))

But you can't do it on non-random-access iterators. This one works with bidirectional iterators:

但是你不能在非随机访问迭代器上做到这一点。这个适用于双向迭代器:

if ((itr != Mine.end()) && (itr == --Mine.end()))

And is safe since end() > itrby the first check.

并且是安全的,因为end() > itr通过第一次检查。

回答by Potatoswatter

Yes, it's unsafe to decrement (or increment) endif the vector may be empty. It's even somewhat unsafe to do the same with a pointer, although you'll probably get away with it.

是的,end如果向量可能为空,则递减(或递增)是不安全的。用指针做同样的事情甚至有点不安全,尽管你可能会侥幸逃脱。

To be really safe, use subtraction and values known to be safe and valid:

为了真正安全,请使用减法和已知安全有效的值:

if ( Mine.end() - itr == 1 )

For compatibility with all forward iterators (such as in slist, as opposed to random-access iterators of vectorand deque), use

与所有前向迭代(例如,在兼容性slist,而不是随机访问迭代vectordeque),使用

if ( std::distance( itr, Mine.end() ) == 1 )

or if you are concerned with performance but have bidirectional iterators (incl. any C++03 container)

或者如果您关心性能但有双向迭代器(包括任何 C++03 容器)

if ( itr != Mine.end() && itr == -- Mine.end() )

or the truly anal case of only forward iterators and O(1) time,

或者只有前向迭代器和 O(1) 时间的真正肛门情况,

if ( itr != Mine.end() && ++ container::iterator( itr ) == Mine.end() )

or if you are hellbent on cleverness to avoid naming the iterator class,

或者,如果你一心想聪明地避免命名迭代器类,

if ( itr != Mine.end() && ++ ( Mine.begin() = itr ) == Mine.end() )

回答by Mark B

Why do you need to do special behavior only if the item is the last one?

为什么只有当项目是最后一项时才需要做特殊行为?

What about this. The plan is just to compare the address of the iterator's item with the address of the last item in the container, with a check to make sure the item is actually not already the end (making the backcall safe):

那这个呢。该计划只是将迭代器项目的地址与容器中最后一个项目的地址进行比较,并检查以确保该项目实际上尚未结束(使back调用安全):

if (itr != Mine.end() && &*itr == &Mine.back()) {
  doSomething;
}

回答by Nikos Athanasiou

You would first need a way to determine if an iterator is a reverse one, which was ingeniously shown here:

您首先需要一种方法来确定迭代器是否为反向迭代器此处巧妙地展示了这一点

#include <iterator>
#include <type_traits>

template<typename Iter>
struct is_reverse_iterator : std::false_type { };

template<typename Iter>
struct is_reverse_iterator<std::reverse_iterator<Iter>>
: std::integral_constant<bool, !is_reverse_iterator<Iter>::value>
{ };

Then you could have two flavors for performing the test

然后你可以有两种口味来执行测试

template<bool isRev> // for normal iterators
struct is_last_it
{
    template<typename It, typename Cont>
    static bool apply(It it, Cont const &cont)
    { // you need to test with .end()
        return it != cont.end() && ++it == cont.end();
    }
};

template<> // for reverse iterators
struct is_last_it<true>
{
    template<typename It, typename Cont>
    static bool apply(It it, Cont const &cont)
    { // you need to test with .rend()
        return it != cont.rend() && ++it == cont.rend();
    }
};

And a single interface function

和单一的接口功能

template<typename It, typename Cont>
bool is_last_iterator(It it, Cont const &cont)
{
    return is_last_it<is_reverse_iterator<It>::value>::apply(it, cont);
};

Then for every type of iterator (reverse / straight) you can use the interface function

然后对于每种类型的迭代器(反向/直),您都可以使用接口函数

int main()
{
    std::vector<int> v;
    v.push_back(1);

    auto it (v.begin()),  ite(v.end());   // normal iterators
    auto rit(v.rbegin()), rite(v.rend()); // reverse iterators

    std::cout << is_last_iterator(it, v) << std::endl;
    std::cout << is_last_iterator(ite, v) << std::endl;
    std::cout << is_last_iterator(rit, v) << std::endl;
    std::cout << is_last_iterator(rite, v) << std::endl;

    return 0;
}

Note that some implementation (apart from std::begin()and std::end()which are common enough, also include std::rbegin()and std::rend(). When possible use this set of functions instead of member .begin()etc.

请注意,某些实现(除了那些足够常见的std::begin()and之外std::end(),还包括std::rbegin()and std::rend()。如果可能,请使用这组函数而不是 member.begin()等。

回答by TheUndeadFish

If you do:

如果你这样做:

if(itr != Mine.end() && itr == --Mine.end())

It should be fine. Because if itr is not at the end then there must be at least 1 element in the container and so end must yield a value result when decremented.

应该没问题。因为如果 itr 不在最后,那么容器中必须至少有 1 个元素,因此当递减时 end 必须产生一个值结果。

But if you still don't like that, there are lots of ways to do something equivalent, as all the other answers show.

但是,如果您仍然不喜欢那样,那么正如所有其他答案所显示的那样,有很多方法可以做等效的事情。

Here's another alternative:

这是另一种选择:

if(itr != Mine.end() && std::distance(Mine.begin(), itr) == Mine.size()-1)

回答by John Dibling

Here's another potential solution:

这是另一个潜在的解决方案:

template<class Iterator, class Container> bool is_last(Iterator it, const Container& cont)
{
    // REQUIREMENTS:
    // the iterator must be a valid iterator for `cont`
    if( it == cont.end() )
        return false;   // or throw if you prefer
    return (++it) == cont.end();
}

回答by Adrian Maire

Trying to make this answer as simple and versatile as possible:

试图使这个答案尽可能简单和通用:

if( itr!=Mine.end() && itr== --Mine.end())

If the iterator is not bi-directional,

如果迭代器不是双向的,

if( itr!=Min.end() && ++decltype(itr)(itr)==Mine.end())

The second create a temporal copy of itr and increment it to test against the end iterator.

第二个创建 itr 的临时副本并增加它以针对结束迭代器进行测试。

In both cases, the first test avoid empty containers to trigger an undefined situation.

在这两种情况下,第一个测试避免空容器触发未定义的情况。

回答by Steven Sudit

A better way would be to copy the iterator and then increment it. You can then test the incremented version against end(). If you're careful, you can use a post-increment to avoid the need to formally copy it.

更好的方法是复制迭代器,然后递增它。然后,您可以针对end(). 如果你小心,你可以使用后增量来避免正式复制它的需要。

  if (++vector<mine*>::iterator(itr) == Mine.end())

If itr could already be at the end:

如果它可能已经在最后:

  if (itr == Mine.end() || ++vector<mine*>::iterator(itr) == Mine.end())

Or, based on GMan's answer but a bit safer:

或者,基于 GMan 的回答但更安全一点:

  if (Mine.Length() == 0 || itr == Mine.End() || &*itr == &Mine.back())

I just fixed the last one again, as I was wrong about &*.

我刚刚又修复了最后一个,因为我错了&*

回答by rmeador

This is essentially the same problem as deleting a node from a singly-linked list. You have to have two iterators, one that follows one node behind the other, so when the "forward" iterator gets to the node you want to delete (or whatever operation; in your case the desired node would be the end), the "following" iterator is pointing to the node before (in your case, this would be the final node).

这与从单向链表中删除节点的问题本质上是相同的。你必须有两个迭代器,一个跟在一个节点后面,所以当“转发”迭代器到达你想要删除的节点(或任何操作;在你的情况下,所需的节点将是结束),“以下”迭代器指向之前的节点(在您的情况下,这将是最终节点)。