Borre / elimine el contenido del mapa (o cualquier otro contenedor STL) mientras lo itera

Supuestamente no se puede simplemente borrar / eliminar un elemento en un contenedor mientras se itera ya que el iterador deja de ser válido. ¿Cuáles son las formas (seguras) de eliminar los elementos que cumplen una determinada condición? por favor solo stl, no boost o tr1.

EDITAR ¿Hay una manera más elegante si quiero borrar una serie de elementos que cumplen ciertos criterios, quizás con el uso de functor y for_each o borrar algoritmo?

bool IsOdd( int i ) { return (i&1)!=0; } int a[] = {1,2,3,4,5}; vector v( a, a + 5 ); v.erase( remove_if( v.begin(), v.end(), bind1st( equal_to(), 4 ) ), v.end() ); // v contains {1,2,3,5} v.erase( remove_if( v.begin(), v.end(), IsOdd ), v.end() ); // v contains {2} 

Puedes hacerlo siempre y cuando no invalides tu iterador después de haberlo borrado:

 MyContainer::iterator it = myContainer.begin(); while(it != myContainer.end()) { if (*it == matchingValue) { myContainer.erase(it++); } else { ++it; } } 

Ejemplo con std :: vector

 #include  using namespace std; int main() { typedef vector  int_vector; int_vector v(10); // Fill as: 0,1,2,0,1,2 etc for (size_t i = 0; i < v.size(); ++i){ v[i] = i % 3; } // Remove every element where value == 1 for (int_vector::iterator it = v.begin(); it != v.end(); /* BLANK */){ if (*it == 1){ it = v.erase(it); } else { ++it; } } } 

La solución de Viktor tiene la ventaja de poder hacer algo con el elemento antes de eliminarlo. (No pude hacer esto con remove_if o remove_copy_if .) Pero prefiero usar std::find_if para no tener que incrementar el iterador por mi cuenta:

 typedef vector int_vector; int_vector v; int_vector::iterator itr = v.begin(); for(;;) { itr = std::find_if(itr, v.end(), Predicate(4)); if (itr == v.end()) { break; } // do stuff with *itr here itr = v.erase(itr); // grab a new, valid iterator } 

Donde predicado podría ser bind1st( equal_to(), 4 ) o algo como esto:

 struct Predicate : public unary_function { int mExpected; Predicate(int desired) : mExpected(desired) {} bool operator() (int input) { return ( input == mExpected ); } }; 

Prefiero la versión con while :

 typedef std::list list_t; void f( void ) { // Remove items from list list_t::iterator it = sample_list.begin(); while ( it != sample_list.end() ) { if ( it->condition == true ) { it = sample_list.erase( it ); } else ++it; } } 

Con eso, no hay peligro de incrementarlo dos veces, ya que podría estar en for bucle for .

1. Para std::vector<> :

 std::vector  vec; vec.erase(std::remove(vec.begin(),vec.end(), elem_to_remove), vec.end()); 

2. Para std::map<> siempre use std::map::erase()

 std::map myMap; myMap.emplace(std::make_pair(1, "Hello")); myMap.emplace(std::make_pair(2, "Hi")); myMap.emplace(std::make_pair(3, "How")); myMap.erase( 1);//Erase with key myMap.erase(myMap.begin(), ++myMap.begin() );//Erase with range for( auto &ele: myMap) { if(ele.first ==1) { myMap.erase(ele.first);//erase by key break; //You can't use ele again properly //wthin this iteration, so break. } } 
  1. Para std::list use std::list::erase()

markh44 es la respuesta más STL-ish. Sin embargo, tenga en cuenta que, en general, los iteradores se invalidan modificando el contenedor, pero el conjunto y el mapa son excepciones. Allí, puede eliminar elementos y seguir usando los iteradores, excepto si elimina el elemento al que hace referencia su iterador.

Use el hecho de que el operador de decremento posterior devuelve una copia del iterador antes de que disminuya. Debido a que el iterador decrementado sigue siendo válido después de borrar el elemento actual, el bucle for continúa operando según lo previsto.

 #include  std::list myList; for(int i = 0; i < 10; ++i ) { myList.push_back(i); } int cnt = 0; for(std::list::iterator iter = myList.begin(); iter != myList.end(); ++iter) { if( cnt == 5 ) { myList.erase(iter--); } ++cnt; } 

Editar: no funciona si intenta borrar el primer elemento de la lista …

 template  void eraseIf( Container& container, Predicate predicate ) { container.erase( remove_if( container.begin(), container.end(), predicate ), container.end() ); } // pre-c++11 version template void eraseIf( std::map& container, Predicate predicate) { typename std::map::iterator iter = container.begin(); while(iter!=container.end()) { iterator current = iter++; if(predicate(*current)) container.erase(current); } } // c++11 version template void eraseIf( std::map& container, Predicate predicate) { auto iter = container.begin(); while(iter!=container.end()) { if(predicate(*iter)) iter = container.erase(iter); else ++iter; } }