Reputation: 1477
The following simplified code works, in that it deletes all vector elements. However, I do not understand why. Since f.erase(r)
does not capture the returned value which would be the new iterator value, and there is no other iterator incrementor, and according to documentation, erase(iterator position)
argument is not passed by reference, where does the iterator get advanced?
#include <iostream>
#include <vector>
int main ()
{
std::vector<int> f = {1,2,3,4,5};
auto r = f.begin();
while (r != f.end())
{
std::cout << "Erasing " << *r << std::endl;
f.erase(r);
}
return 0;
}
Upvotes: 0
Views: 2279
Reputation: 311156
You have to write
while (r != f.end())
{
std::cout << "Erasing " << *r << std::endl;
r = f.erase(r);
^^^^^^^^^^^^^^^^
}
because after erasing the iterator becomes invalid.
Or you could just write
f.clear();
because the loop removes all elements of the vector.
Take into account that as the iterator r is used only in the loop it is better to declare it in the scope of the loop that is where it is used. for example
for ( auto r = f.begin(); r != f.end(); )
{
std::cout << "Erasing " << *r << std::endl;
r = f.erase(r);
}
Upvotes: 2
Reputation: 7374
Adding/removing elements to/from vector in most cases (that includes erase())invalidates references and iterators. Using old iterators results in undefined behavior.
As @Nathan mentioned in the comments f.clear()
is all you need.
Upvotes: 1
Reputation: 38209
You should read it to the end:
Because vectors use an array as their underlying storage, erasing elements in positions other than the vector end causes the container to relocate all the elements after the segment erased to their new positions. This is generally an inefficient operation compared to the one performed for the same operation by other kinds of sequence containers (such as list or forward_list).
and
Return value
An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
And finally:
Iterator validity
Iterators, pointers and references pointing to position (or first) and beyond are invalidated, with all iterators, pointers and references to elements before position (or first) are guaranteed to keep referring to the same elements they were referring to before the call.
Upvotes: 1
Reputation: 96976
erase
invalidates r
. Dereferening r
after that causes undefined behavior.
But in the real world, it shouldn't cause any problems unless the implementation intentionally checks iterator validity.
A vector
iterator normally only stores a pointer to the element. When you delete an element, all elements to the right of it are shifted to the left by one position. Because of that, the memory location that used to be occupied by the deleted element, will be occupied by the next element.
Upvotes: 0
Reputation: 181068
where does the iterator get advanced?
It doesn't, the iterator stays pointing to the same location. This is technically undefined behavior but if you think about what the loop is actually doing, you'll see why you get the "correct" results.
Your vector holds a pointer to the objects that it stores. Your iterator is going to point to that memory with the offset to the element you want. In this case it is going to point to the beginning of the data. When you erase the first element, the iterator is invalidated but it is still pointing to the beginning of the vector. erase
moves all of the elements forward so when you go into the next iteration, your in the same state that you were in the first iteration except the vector is one element smaller. You repeatedly do this until there are no elements left and end() == begin()
You should not depend on this always happening though and instead just use clear()
to remove all of the elements from the vector.
Upvotes: 2
Reputation: 238461
Passing the iterator to erase
invalidates that iterator, and therefore using it further (when passing it to erase
in the next iteration) has undefined behaviour. Thus, the program doesn't actually "work". It may appear to work, because that is one possible behaviour. But that behaviour is not guaranteed.
Upvotes: 0
Reputation: 123488
where does the iterator get advanced?
The iterator does not get advanced. The fact that your code seems to work is just by chance and in fact you have undefined behaviour.
From cppreference on std::vector::erase
:
Invalidates iterators and references at or after the point of the erase, including the end() iterator.
You are not allowed to use r
after calling f.erase(r);
. If you do, funny things can happen.
Upvotes: 2