Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing whether an iterator points to the last item?

Tags:

c++

iterator

stl

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:

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.

like image 793
WilliamKF Avatar asked Aug 18 '10 20:08

WilliamKF


People also ask

How do you know if an iterator is last element?

You can check it just like you do in the while condition: if ( ! iter. hasNext()) { // last iteration ... }

How do you point iterator to the last element?

To get the last element in an iterator loop you can use std::next() (from C++11). The loop is generally terminated by iterator != container. end() , where end() returns an iterator that points to the past-the-end element.

What does iterator end point to?

By definition, end() returns an iterator "one past the last element"; in other words, if an iterator designates the last element, incrementing it causes it to be equal to end() .

What happens if you increment the iterator past-the-end?

Obviously if the iterator is advanced past the last element inside the loop the comparison in the for-loop statement will evaluate to false and the loop will happily continue into undefined behaviour.


2 Answers

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() > itr by the first check.

like image 149
GManNickG Avatar answered Oct 15 '22 21:10

GManNickG


Yes, it's unsafe to decrement (or increment) end if 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.

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 vector and deque), use

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

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

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

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

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() ) 
like image 20
Potatoswatter Avatar answered Oct 15 '22 23:10

Potatoswatter