What Happens If You Call Erase() on a Map Element While Iterating from Begin to End

What happens if you call erase() on a map element while iterating from begin to end?

C++11

This has been fixed in C++11 (or erase has been improved/made consistent across all container types).

The erase method now returns the next iterator.

auto pm_it = port_map.begin();
while(pm_it != port_map.end())
{
if (pm_it->second == delete_this_id)
{
pm_it = port_map.erase(pm_it);
}
else
{
++pm_it;
}
}

C++03

Erasing elements in a map does not invalidate any iterators.

(apart from iterators on the element that was deleted)

Actually inserting or deleting does not invalidate any of the iterators:

Also see this answer:

Mark Ransom Technique

But you do need to update your code:

In your code you increment pm_it after calling erase. At this point it is too late and is already invalidated.

map<string, SerialdMsg::SerialFunction_t>::iterator pm_it = port_map.begin();
while(pm_it != port_map.end())
{
if (pm_it->second == delete_this_id)
{
port_map.erase(pm_it++); // Use iterator.
// Note the post increment.
// Increments the iterator but returns the
// original value for use by erase
}
else
{
++pm_it; // Can use pre-increment in this case
// To make sure you have the efficient version
}
}

How to remove from a map while iterating it?

The standard associative-container erase idiom:

for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}

Note that we really want an ordinary for loop here, since we are modifying the container itself. The range-based loop should be strictly reserved for situations where we only care about the elements. The syntax for the RBFL makes this clear by not even exposing the container inside the loop body.

Edit. Pre-C++11, you could not erase const-iterators. There you would have to say:

for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }

Erasing an element from a container is not at odds with constness of the element. By analogy, it has always been perfectly legitimate to delete p where p is a pointer-to-constant. Constness does not constrain lifetime; const values in C++ can still stop existing.

Erase other elements of std::map while iterating

You need to read the iterator invalidation rules:

std::map::erase

References and iterators to the erased elements are invalidated. Other
references and iterators are not affected.

So as long as you don't use the iterator where you erased, you are good.

Is it safe to invoke std::map::erase with std::map::begin?

begin() is not an iterator but returns an iterator. After erasing the first element, begin()returns another (valid) iterator.

 std::map<int, int> m;
m[1] = 2;
m[2] = 3;
m.erase(m.begin()); // <- begin() points to 1:2
std::cout << m.begin()->second; // <- begin() points to 2:3 now

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

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.

C++ loop through map while erasing

http://en.cppreference.com/w/cpp/container/map/erase :

References and iterators to the erased elements are invalidated. Other
references and iterators are not affected.

(So make sure you increment and save a "next" iterator before you erase.

Edit: In fact since C++11, erase returns the next iterator anyway, so you may use that.)

Erasing while traversing in C++ stl map giving runtime error

When you are calling erase(v), you are invalidating the base iterator that the next reverse_iterator (from ++it) is using. So you need to create a new reverse_iterator from the base iterator that precedes the erased value.

Also, rather than erasing the value that the reverse_iterator is referring to, you should erase the base iterator instead, since you already know which element you want to erase. There is no need to make the map go hunting for the value again.

This works for me:

map<int,int> mymap = {{1,0},{2,1},{9,2},{10,3},{11,4}};
for(auto it = mymap.rbegin(); it != mymap.rend(); ){
auto v = --(it.base());
v = mymap.erase(v);
it = map<int,int>::reverse_iterator(v);
}

Demo

On the other hand, this loop is essentially just erase()'ing all elements from mymap, so a better option is to use mymap.clear() instead.

Erase/Remove contents from the map (or any other STL container) while iterating it

bool IsOdd( int i )
{
return (i&1)!=0;
}

int a[] = {1,2,3,4,5};
vector<int> v( a, a + 5 );
v.erase( remove_if( v.begin(), v.end(), bind1st( equal_to<int>(), 4 ) ), v.end() );
// v contains {1,2,3,5}
v.erase( remove_if( v.begin(), v.end(), IsOdd ), v.end() );
// v contains {2}


Related Topics



Leave a reply



Submit