tags:

views:

2485

answers:

4

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.

+10  A: 

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-stdmap

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
    }
}
Martin York
A: 

You could iterate from end to beginning to avoid errors

Robo
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.
MSalters
+1  A: 

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_id

but I just copied it from your example code.

ravenspoint
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'
Martin York
A: 

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 ...
     }
}
AlaaShaker