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}
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.
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
}
}
Find and erase element from STL container while iterating
From cppreference on unordered_multimap::erase
:
References and iterators to the erased elements are invalidated. Other iterators and references are not invalidated.
So I think that if you get an iterator to secondPair
and secondPairIt != it
then you can safely erase secondPairIt
. You should also check that you are not invalidating the end of the range.
for (auto it = range.first; it != range.second;)
{
if (condition)
{
auto secondPairIt = getSecondPairIt(it, myMultiMap); // Assume this is not end
if (secondPairIt != it)
{
if (secondPairIt == range.second)
range.second = myMultiMap.erase(secondPairIt);
else
myMultiMap.erase(secondPairIt);
}
it = myMultiMap.erase(it);
}
else
{
++it;
}
}
C++11: Is it safe to remove individual elements from std::unordered_map while iterating?
Edit: The dangers of NoScript. I had noscript running, which displayed the C11 and C14 tabs as one box. Praetorian's answer is correct about it being guaranteed in practice, and formalized in c14.
** Below is wrong due to noscript.
At the bottom of cplusplus it states that
Only the iterators and references to the elements removed are invalidated.
The rest are unaffected.
The relative order of iteration of the elements not removed by the operation is preserved.
http://www.cplusplus.com/reference/unordered_map/unordered_map/erase/
At the top of the page, it states it's for C++11...so unless they updated it for C++14, i think it applies to C++11 as well. Praetorian should put an answer and you should check his for the answer, because even if it's not guaranteed in the standard for C++11 (C++14 being the patch for these sort of things), it's guaranteed in practice.
I couldn't find the STL standard, I seem to have misplaced it, or I'd go see if there was a text guaranteed I could point to. :-/
Deleting elements from std::set while iterating
This is implementation dependent:
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.
Maybe you could try this -- this is standard conforming:
for (auto it = numbers.begin(); it != numbers.end(); ) {
if (*it % 2 == 0) {
numbers.erase(it++);
}
else {
++it;
}
}
Note that it++ is postfix, hence it passes the old position to erase, but first jumps to a newer one due to the operator.
2015.10.27 update:
C++11 has resolved the defect. iterator erase (const_iterator position);
return an iterator to the element that follows the last element removed (or set::end
, if the last element was removed). So C++11 style is:
for (auto it = numbers.begin(); it != numbers.end(); ) {
if (*it % 2 == 0) {
it = numbers.erase(it);
}
else {
++it;
}
}
Erase element iterating in a while loop
You have undefined behaviour, because you are dereferencing an invalid iterator at delete *it;
. To fix your immediate problem, you need something like
int * to_del = *it;
it = queue.erase(it);
delete to_del;
However, you should also avoid using int *
where int
is sufficient, and writing loops when there is something applicable in <algorithm>
bool is_even(int i) { return (i % 2) == 0; }
std::deque<int> queue { 0, 2, 3, 4, 5 };
queue.erase(std::remove_if(queue.begin(), queue.end(), is_even), queue.end());
Can you remove elements from a std::list while iterating through it?
You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
Related Topics
Explicit Return Type of Lambda
Qobject: Cannot Create Children for a Parent That Is in a Different Thread
Is Returning References of Member Variables Bad Practice
How to Read a File into Vector in C++
How to Use Libraries Compiled with Mingw in Msvc
Literal Initialization for Const References
How to Access Variables Defined and Declared in One Function in Another Function
Is Make_Shared Really More Efficient Than New
Std::Vector Reserve() and Push_Back() Is Faster Than Resize() and Array Index, Why
How Do Memory_Order_Seq_Cst and Memory_Order_Acq_Rel Differ
Template Specialization Based on Inherit Class
Difference Between A* Pa = New A; and A* Pa = New A();
What Is Better: Reserve Vector Capacity, Preallocate to Size or Push Back in Loop