What if I increment an iterator by 2 when it points onto the last element of a vector? In this question asking how to adjust the iterator to an STL container by 2 elements two different approaches are offered:
I've tested both of them with VC++ 7 for the edge case when the iterator points onto the last element of the STL container or beyond:
vector<int> vec; vec.push_back( 1 ); vec.push_back( 2 ); vector<int>::iterator it = vec.begin(); advance( it, 2 ); bool isAtEnd = it == vec.end(); // true it++; // or advance( it, 1 ); - doesn't matter isAtEnd = it == vec.end(); //false it = vec.begin(); advance( it, 3 ); isAtEnd = it == vec.end(); // false
I've seen may times an advise to compare against vector::end() when traversing the vector and other containers:
for( vector<int>::iterator it = vec.begin(); it != vec.end(); it++ ) { //manipulate the element through the iterator here }
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 I get it right that if I ever use advance() or any kind of increment operation on an iterator and make it point past the container's end I will be unable to detect this situation? If so, what is the best practice - not to use such advancements?
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.
In something like an std::vector the ::end() iterator will point to one past the last element. You can't dereference this iterator but you can compare it to another iterator. If you compare another iterator to end() you know you've reached the end of the container.
In C++, you cannot dereference an iterator straight away because the end() function returns an iterator and object as a pointer, which isn't a valid member of the data structure.
It appears that you can still decrement the iterator returned from end() and dereference the decremented iterator, as long as it's not a temporary.
Following is the quote from Nicolai Josuttis book:
Note that advance() does not check whether it crosses the end() of a sequence (it can't check because iterators in general do not know the containers on which they operate). Thus, calling this function might result in undefined behavior because calling operator ++ for the end of a sequence is not defined
In other words, the responsibility of maintaining the iterator within the range lies totally with the caller.
Perhaps you should have something like this:
template <typename Itr> Itr safe_advance(Itr i, Itr end, size_t delta) { while(i != end && delta--) i++; return i; }
You can overload this for when iterator_category<Itr>
is random_access_iterator
to do something like the following:
return (delta > end - i)? end : i + delta;
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