C++ map container erase() segmentation fault [duplicate] - c++

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;
});

Related

Delete elements from a vector in C++11 while iterating over it [duplicate]

This question already has answers here:
Erasing elements from a vector
(6 answers)
Closed 6 years ago.
My applications requires to iterate over a vector and delete certain elements which doesnt satisfy the requirements. Which is the most correct way? I believe the following way is incorrect.Reason: I am getting segmentation fault.
std::vector<ObjectX> vec1;
//Fill in vec1
std::vector<ObjectX>::iterator itVec1 = vec1.begin();
for(;itVec1 != vec1.end(); ++itVec1) {
if (Oracle(*itVec1)) vec1.erase(itVec1);
}
When you call
vec1.erase(itVec1);
you invalidate itVec1. After that, ++itVec1 is not right. It leads to undefined behavior. You need to change your code a little bit.
for( ; itVec1 != vec1.end(); ) {
if (Oracle(*itVec1))
{
itVec1 = vec1.erase(itVec1);
}
else
{
++itVec1;
}
}
You can remove all the boiler plate code by using the Erase-Remove Idiom:
vec1.erase(std::remove_if(vec1.begin(), vec1.end(), Oracle), vec1.end());
You could just put the correct items into a new vector.

Delete all entries in STL Map which matches value [duplicate]

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.

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;
}

C++ loop through map while erasing [duplicate]

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 9 years ago.
To loop through a map in c++ we do sth like this
map<string,int> mymap;
map<string,int>::iterator it= mymap.begin();
while(it!=mymap.end()) {
//code here
it++;
}
What if in the "code here" part i have an if statement that if evaluated to true, it erases one element from the map? How should my code change so that it still loops through all mymap elements in order?
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.)
you may want to reassign your iterator when you erase an element, as it wont be valid otherwise...
it = mymap.erase(...)
To avoid using the iterator after invalidating it when erasing, the loop body should be like this:
if (should_erase) {
it = my_map.erase(it); // C++11: returns the next iterator
my_map.erase(it++); // Historic C++: no helpful return value
} else {
++it;
}

Post increment on set iterator [duplicate]

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