In the following code I loop through a map and test if an element needs to be erased. Is it safe to erase the element and keep iterating or do I need to collect the keys in another container and do a second loop to call the erase()?
map<string, SerialdMsg::SerialFunction_t>::iterator pm_it;
for (pm_it = port_map.begin(); pm_it != port_map.end(); pm_it++)
{
if (pm_it->second == delete_this_id) {
port_map.erase(pm_it->first);
}
}
UPDATE: Of course, I then read this question which I didn't think would be related but answers my question.
-
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:
http://stackoverflow.com/questions/180516/how-to-filter-items-from-a-stdmapBut 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 } } -
You could iterate from end to beginning to avoid errors
MSalters : Doesn't help at all. The only iterator invalidated is the iterator erased. You can neither increment it or decrement it afterwards. pm_it-- is equally wrong. -
This is how I would do it, approximately:
bool is_remove( pair<string, SerialdMsg::SerialFunction_t> val ) { return val.second == delete_this_id; } map<string, SerialdMsg::SerialFunction_t>::iterator new_end = remove_if (port_map.begin( ), port_map.end( ), is_remove ); port_map.erase (new_end, port_map.end( ) );There is something odd about
val.second == delete_this_idbut I just copied it from your example code.
Martin York : Use the map value_type in the is_remove. Also prefer functor to function (Easier for the compiler to optimize) and you can bind different values to 'delete_this_id' -
Here's how I do that ...
typedef map<string, string> StringsMap; typedef StringsMap::iterator StrinsMapIterator; StringsMap m_TheMap; // Your map, fill it up with data bool IsTheOneToDelete(string str) { return true; // Add your deletion criteria logic here } void SelectiveDelete() { StringsMapIter itBegin = m_TheMap.begin(); StringsMapIter itEnd = m_TheMap.end(); StringsMapIter itTemp; while (itBegin != itEnd) { if (IsTheOneToDelete(itBegin->second)) // Criteria checking here { itTemp = itBegin; // Keep a reference to the iter ++itBegin; // Advance in the map m_TheMap.erase(itTemp); // Erase it !!! } else ++itBegin; // Just move on ... } }
0 comments:
Post a Comment