This question already has answers here:
How to remove from a map while iterating it?
(6 answers)
Erase/Remove contents from the map (or any other STL container) while iterating it
(9 answers)
Closed 8 years ago.
I'm trying to delete all entries in a map where value == 50 for any key.
This code is working fine for me.
while (itr != mymap.end())
{
if ((*itr).second == 50)
mymap.erase(itr++);
else
itr++;
}
But this code is giving run time error.
while (itr != mymap.end())
{
if ((*itr).second == 50)
{
mymap.erase(itr);
itr++
}
else
itr++;
}
My doubt is aren't both logic is same? why run time error in second case?
No, the logic is not the same.
In the first case the iterator is postincremented before erasing the element when it is a valid iterator.
In the second case the iterator is postincremented after erasing the element when it is an invalid iterator.
The common approach to this operation is the following
while ( itr != mymap.end() )
{
if ( (*itr).second == 50 )
itr = mymap.erase( itr );
else
itr++;
}
According to the C++ Standard (23.2.4 Associative containers)
9 The insert and emplace members shall not affect the validity of
iterators and references to the container, and the erase members
shall invalidate only iterators and references to the erased
elements.
Related
This question already has answers here:
How to remove from a map while iterating it?
(6 answers)
Closed 2 years ago.
I use this code to remove elements from map container that equal to some int.
for(auto x:m){
if((x.second)==element)m.erase(x.first);
}
As result Segmentation fault. I also tried this:
for(map<int,int>::iterator i=m.begin();i!=m.end();i++){
if((i->second)==element)m.erase(i);
}
Same result. If you put i++ into if/else program will freeze/loop or something. How can I fix this?
erase() invalidates the iterator being used by the for loop. Fortunately, erase() itself returns an iterator to the next entry, so the correct loop would look like this instead:
for (map<int,int>::iterator i = m.begin(); i != m.end(); )
{
if (i->second == element)
i = m.erase(i);
else
++i;
}
In addition to #john's answer, if your C++ Standard Library implementation supports it, you can invoke the std::erase_if(map, condition) helper:
std::erase_if(m, [](const auto& item) {
auto const& [key, value] = item;
// Write your erasing condition here, e.g.:
// return value == element;
});
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;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
iterator validity ,after erase() call in std::set
When I iterate over a set and want to delete certain items the iterators are changed. This results in segfaults as the iteration fails after deletion. How can I overcome this problem?
std::set< std::pair<double,unsigned>, comparisonFunction> candidates;'
[...]
for( auto it = candidates.begin(); it != candidates.end(); ++it)
{
[...]
if ( some constraint satisfied)
{
candidates.erase(it);
}
}
I encounter a segfault when I use this code. My guess is that this is either due to the corrupted iterators or due to the fact, that the element to be deleted is the last element in some cases. Does a post increment on the iterator overcome this problem? Like this:
candidate.erase(it++);
Use the return value of erase:
it = candidates.erase(it);
Note that you must not increment it if you erase an element, otherwise your iterator could be invalidated.
for( auto it = candidates.begin(); it != candidates.end();)
{
if ( some constraint satisfied)
{
it = candidates.erase(it);
}
else
++it;
}
Also note that this wasn't possible in C++03, since erase didn't return any iterator. However, since you're using C++11 it shouldn't be a problem.
References
std::set::erase
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;
}
}
This question already has answers here:
What happens if you call erase() on a map element while iterating from begin to end?
(3 answers)
Closed 8 years ago.
Can I continue to use an multimap iterator even after a call to multimap::erase()? For example:
Blah::iterator iter;
for ( iter = mm.begin();
iter != mm.end();
iter ++ )
{
if ( iter->second == something )
{
mm.erase( iter );
}
}
Should this be expected to run correctly, or is the iterator invalidated following the call to erase? Reference sites like http://www.cplusplus.com/reference/stl/multimap/erase.html are strangely quiet on this topic of the lifespans of iterators, or the effects of constructive/destructive methods on iterators.
http://www.sgi.com/tech/stl/Multimap.html
Multimap has the important property that inserting a new element
into a multimap does not invalidate iterators that point to existing
elements. Erasing an element from a multimap also does not invalidate
any iterators, except, of course, for iterators that actually point to
the element that is being erased.
So it should look like this:
Blah::iterator iter;
for ( iter = mm.begin();iter != mm.end();)
{
if ( iter->second == something )
{
mm.erase( iter++ );
// Use post increment. This increments the iterator but
// returns a copy of the original iterator to be used by
// the erase method
}
else
{
++iter; // Use Pre Increment for efficiency.
}
}
Also see:
What happens if you call erase() on a map element while iterating from begin to end?
and
delete a specific entry in the map,but the iterator must point to the next element after the deletion
C++ Standard 23.1.2.8:
The insert members shall not affect the validity of iterators and references to the container, and the erase members shall
invalidate only iterators and references to the erased elements.
This is a common requirement for all associative containers, and std::multimap is one of them.