Does end iterator get update when inserting a new element? [duplicate] - c++

This question already has answers here:
Iterator invalidation rules for C++ containers
(6 answers)
Closed 3 years ago.
I cannot figure why I'm getting an error when running the following simple program
#include <iostream>
#include <vector>
int main(int argc, char** argv) {
std::vector<int> v{ 1,2,3,4,5 };
std::vector<int>::iterator it1 = v.end();
auto it_tmp = v.insert(v.end(), 6);
std::vector<int>::iterator it2 = v.end();
std::cout << (it1 == it2) << std::endl;
return 0;
}
The iterators it1 and it2 are incompatible, so I was wondering what could possible be the issue. Iterators are incompatible if they belong to different containers, I would then assume in my case one of the two gets invalidated or something like that (I've also try to change v.end with v.begin() for both it1 and it2, it doesn't make any difference).
Thank you.

it1 == it2 evaluates false because after this auto it_tmp = v.insert(v.end(), 6);, end iterator changes.
std::vector::insert inserts before the given iterator. Everything before the insertion point remains valid. Everything after it is invalidated. it1 here is invalidated:
std::vector::insert
Causes reallocation if the new size() is greater than the old
capacity(). If the new size() is greater than capacity(), all
iterators and references are invalidated. Otherwise, only the
iterators and references before the insertion point remain valid. The
past-the-end iterator is also invalidated.

Related

Does modifying a std::vector invalidate an iterator? [duplicate]

This question already has answers here:
Iterator invalidation rules for C++ containers
(6 answers)
Closed 2 years ago.
Consider this piece of code:
#include <vector>
#include <iostream>
int main(int argc, char** args)
{
std::vector<int> vec;
vec.push_back(0);
for (auto iter = vec.begin(); iter != vec.end(); iter++)
{
vec.push_back((*iter) + 1);
std::cout << *iter << std::endl;
}
}
I expected this to print all the numbers to Infinite. But what it did was to print a LOT of zeros (and an occasional -256, WHAT?) only to run into a segfault.
My assumtion is that iter still points to the old array after the call to vec.push_back moves the internal data to a new array.
What exactly is the problem here? Is my assumption correct, that the push_back call invalidates the iterator?
Interestingly, when I substitute std::vector for a std::list, the code works as expected. Is it save to use a std::list instead? Or is that just working correctly by accident?
std::vector::push_back invalidates all iterators on that vector if the new vector size is greater than the previous capacity. (The reason is due to the reallocation of the vector).
The behaviour of using an invalidated iterator is undefined.
std::list::push_back does not invalidate any iterators.

Validity of list begin iterator after insertion

Consider the following program:
#include <list>
#include <cstdio>
int main() {
std::list<int> l;
std::list<int>::iterator it = l.begin();
l.push_back(0);
l.insert(it, 1);
for(const int &i: l) {
printf("%d", i);
}
}
(http://cpp.sh/66giy)
This prints 01. Very surprising. If I change the list to a deque, it prints the expected 10.
Is this a bug?
EDIT: The deque behavior is irrelevant, iterators to deques are invalidated by push_back.
I can't catch your problem... ok, lets try to reproduce:
std::list<int> l;
std::list<int>::iterator it = l.begin();
What is your iterator pointing to? To the end of the list as the list is empty!
ยง23.2.1 [container.requirements.general] p6
begin() returns an iterator referring to the first element in the container. end() returns an iterator which is the past-the-end value for the container. If the container is empty, then begin() == end();
l.push_back(0);
Now the list contains a single element. Your iterator is valid as a list did not invalidate the iterator and points still to the end of the list.
l.insert(it, 1);
Now you insert 1 before the iterator which points still to the end. So your first element is a 0 and the last one is a 1.
So your output is 01 as expected.
Maybe your expectation that the begin() delivers a fixed virtual start of container iterator is simply wrong?

Strange/interesting behaviour with std::vector iterators and resizing/reserving

I have two very similar bits of code; this:
std::vector<int> fail{0};
fail.reserve(2);
std::vector<int>::iterator it1 = fail.begin(), it2 = fail.begin() + 1;
fail.push_back(0);
it1 == it2;
which throws a "vector iterators incompatible" exception and this:
std::vector<int> fail{0, 0};
fail.reserve(3);
std::vector<int>::iterator it1 = fail.begin(), it2 = fail.begin() + 1;
fail.push_back(0);
it1 == it2;
which doesn't. It seems to be due to the it2 being the end of the vector in the first example but not in the second, but I'd just like to get a full clarification for why the first throws but the second doesn't.
For reference I am using MSVC.
std::vector::push_back always invalidates the past-the-end iterator, so in the first case it2. This happens regardless of resizing.
All other iterators stay intact if the vector does not reallocate, that's why they second snippet is fine.

Does std::map::erase(it++) maintain a valid iterator pointing to the next element in the map? [duplicate]

This question already has answers here:
How to remove from a map while iterating it?
(6 answers)
Closed 8 years ago.
I'm using Xcode with C++ 11 for a std::map. Some elements in my map have a flag that says they need to be removed.
I want to iterate through the map, erasing the flagged elements in O(n) time. The call to erase does not return an iterator. I have seen some kind of erase(it++) implementation, but I have no evidence that such a call can work since the iterator will become invalid after the erase operation but before the increment operation.
My current code seems so inefficient.
for(auto it = myMap.begin(); it != myMap.end(); ++it)
{
delete *it;
myMap.erase(it);
it = myMap.begin(); //how can I avoid iterating through the map again
}
From the online documentation:
"Iterators, pointers and references referring to elements removed by the function are invalidated. All other iterators, pointers and references keep their validity."
So maybe this:
for(auto it = myMap.begin(); it != myMap.end();)
{
auto itPrev = it;
++it;
if(shouldBeDeleted(*itPrev))
myMap.erase(itPrev);
}
Edit: The erase(it++) idea you mention is actually ok, because the increment occurs (and returns a copy of the old, pre-increment value) before erase() is called. It's in effect the equivalent of:
template<typename IteratorT>
IteratorT PostIncrement(IteratorT& it)
{
auto copy = it;
++it;
return copy;
}
for(auto it = myMap.begin(); it != myMap.end();)
myMap.erase(PostIncrement(it));
which amounts to the same thing as the other example. Incidentally, this is why you should normally use the prefix ++ with iterators; that copy operation is extra overhead, and you usually don't need it.
When std::map::erase() is passed an iterator, it returns an iterator to the next element that follows the element being erased. This allows you to continue your iteration without starting over.
Try this:
auto it = myMap.begin();
while (it != myMap.end())
{
if (it->flagged)
{
delete *it;
it = myMap.erase(it);
}
else
++it;
}

What happens with `map::iterator` when i remove entry from map? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to filter items from a std::map?
What happens if you call erase() on a map element while iterating from begin to end?
I have map of objects and i want to iterate over it and remove some entries.
typedef std::map<A,B> MapT;
MapT m;
MapT::iterator it;
for(it = m.begin(); it != m.end(); it++ ) {
if( condition ) m.erase(it);
}
Can I do it in this way?
In case of std::map iterators and references to the erased elements are invalidated [23.1.2/8]. Your code uses the iterator after it has been invalidated, this results in an Undefined Behavior. In order to avoid this Undefined behavior the iterator needs to be incremented before it gets invalidated in the erase() call.
You need to use:
for(it = m.begin(); it != m.end(); ) {
if( condition )
m.erase(it++);
else
++it;
}
Note that here it++ increments it so that it refers to the next element but yields a copy of its original value. Thus, it doesn't refer to the element that is removed when erase() is called.
Assuming MapT is a std::map, then the iterator will be invalidated when erased. The correct way to do this (erase in a loop) is to cache the iterator and increment prior to erasing it:
MapT m;
MapT::iterator it;
for(it = m.begin(); it != m.end();)
{
if(condition)
{
MapT::iterator tmp = it++;
m.erase(tmp);
}
else
{
++it;
}
}