Why std::vector iterator is invalidated after the

2020-02-10 08:29发布

问题:

The C++ reference clearly state that calling std::vector::erase(it) on iterator will invalidate all iterators pointing to and after the erased element. http://en.cppreference.com/w/cpp/container/vector/erase

I do understand why such iterators became non-dereferenceable after the erase call, but i am curious why they need to became invalid, what implementation details require it?

For instance standard says that std::vector must be implemented with elements stored contiguously and that elements can be accessed not only through iterators, but also using offsets on regular pointers to elements so it seems logical that iterators for such container will probably be implemented as pointers - but then how pointers could became invalidated?

回答1:

One of the principles on which the conceptual idea of iterator is built, is as follows: as long as iterator remains non-aliased, dereferenceable and non-modified, it should refer to the same entity. In other words, dereferencing the same iterator multiple times should yield the same value. Algorithms that use iterators may rely on that.

What you proposing would result in an iterator that would "magically" change the value it refers to even though the iterator itself remains unchanged. This is not acceptable within the conceptual idea of iterator.


On the second thought, what I said above is obviously flawed in a sense that we can always apply some modifying operation to the vector that shifts elements around (e.g. std::random_shuffle). Such operation would not invalidate any iterators, but would easily change the values the iterators refer to. How is that different from element shifting triggered by erase? It isn't.



回答2:

"invalidated" can mean "no longer points to what it used to", not just "may not point to any valid element"

consider (uncompiled code):

vector<int> v = {0, 1, 2, 3, 4, 5};
vector<int>::iterator iter = v.begin() + 3;  // "points to" 3
assert(*iter == 3);
v.erase(v.begin());

At this point, iter has been invalidated. It no longer "points to" the same element that it did before.



回答3:

std::vector must be implemented with elements stored contiguously

This is the reason. If you erase an element inside the vector, the elements, at the least, must be shifted. You could, not with debug protection:

std::vector< int > test= {1,2,3,4,5,6,7};
auto it= test.begin( ) + 2;
test.erase( it );
std::cout << *it << std::endl;

And it will likely print '4'. But there is no guaranty. What if the vector reallocs? What if you erase test.begin( ) + 6 ? If you change the size of a vector, it can be moved.

More Info



回答4:

I simply see no reason for the iterators to become invalid with the point where I begin to erase the elements. vector::erase( ... ) does use the assignment-opetor, so the objects in the vector are never invalidated. If I would do the same with my own code ...

template<typename T>
void vector_erase( vector<T> &v, typename vector<T>::iterator first, typename vector<T>::iterator last )
{
    typename vector<T>::iterator shiftOld,
                                 shiftNew;
    for( shiftOld = last, shiftNew = first; shiftOld != v.end(); ++shiftOld, ++shiftNew )
        *shiftNew = move( *shiftOld );
    v.resize( shiftNew - v.begin() );
}

... the iterators would be valid until the point where I cut the vector.