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.
You can check it just like you do in the while condition: if ( ! iter. hasNext()) { // last iteration ... }
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.
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() .
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.
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.
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() )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With