¿Quitar el elemento del vector, mientras que en el rango de C ++ 11 ‘for’ loop?

Tengo un vector de IInventory *, y estoy recorriendo la lista usando el rango de C ++ 11, para hacer cosas con cada uno.

Después de hacer algunas cosas con una, es posible que desee eliminarlo de la lista y eliminar el objeto. Sé que puedo llamar delete en el puntero en cualquier momento para limpiarlo, pero ¿cuál es la forma correcta de eliminarlo del vector, mientras estoy en el rango for ciclo? Y si lo elimino de la lista ¿mi bucle será invalidado?

 std::vector inv; inv.push_back(new Foo()); inv.push_back(new Bar()); for (IInventory* index : inv) { // Do some stuff // OK, I decided I need to remove this object from 'inv'... } 

No, no puedes. El rango for es para cuando necesita acceder a cada elemento de un contenedor una vez.

Debería usar el ciclo for normal o uno de sus primos si necesita modificar el contenedor a medida que avanza, acceder a un elemento más de una vez o iterar de forma no lineal a través del contenedor.

Por ejemplo:

 auto i = std::begin(inv); while (i != std::end(inv)) { // Do some stuff if (blah) i = inv.erase(i); else ++i; } 

Cada vez que se elimina un elemento del vector, debe asumir que los iteradores en o después del elemento borrado ya no son válidos, porque cada uno de los elementos que suceden al elemento borrado se mueve.

Un bucle for basado en el rango es solo azúcar sintáctica para bucle “normal” usando iteradores, por lo que se aplica lo anterior.

Dicho esto, podrías simplemente:

 inv.erase( std::remove_if( inv.begin(), inv.end(), [](IInventory* element) -> bool { // Do "some stuff", then return true if element should be removed. return true; } ), inv.end() ); 

Lo ideal es que no modifiques el vector mientras lo iteras. Usa el idioma borrar-eliminar. Si lo haces, es probable que encuentres algunos problemas. Dado que en un vector un erase invalida todos los iteradores que comiencen con el elemento borrado hasta el end() , deberá asegurarse de que sus iteradores sigan siendo válidos mediante:

 for (MyVector::iterator b = v.begin(); b != v.end();) { if (foo) { b = v.erase( b ); // reseat iterator to a valid value post-erase else { ++b; } } 

Tenga en cuenta que necesita la prueba b != v.end() tal como está. Si intenta optimizarlo de la siguiente manera:

 for (MyVector::iterator b = v.begin(), e = v.end(); b != e;) 

se encontrará con UB ya que su e se invalida después de la primera llamada de erase .

¿Es un requisito estricto eliminar elementos mientras está en ese bucle? De lo contrario, podría establecer los punteros que desea eliminar en NULL y hacer otra pasada sobre el vector para eliminar todos los punteros NULL.

 std::vector inv; inv.push_back( new Foo() ); inv.push_back( new Bar() ); for ( IInventory* &index : inv ) { // do some stuff // ok I decided I need to remove this object from inv...? if (do_delete_index) { delete index; index = NULL; } } std::remove(inv.begin(), inv.end(), NULL); 

lo siento por necroposting y también lo siento si mi experiencia en c ++ se interpone en el camino de mi respuesta, pero si tratas de iterar a través de cada elemento y hacer posibles cambios (como borrar un índice), intenta usar un backwords for loop.

 for(int x=vector.getsize(); x>0; x--){ //do stuff //erase index x } 

al borrar el índice x, el siguiente ciclo será para el ítem “al frente” de la última iteración. realmente espero que esto haya ayudado a alguien

OK, llegué tarde, pero de todos modos: Lo siento, no corrijo lo que leí hasta ahora: es posible, solo necesitas dos iteradores:

 std::vector::iterator current = inv.begin(); for (IInventory* index : inv) { if(/* ... */) { delete index; } else { *current++ = index; } } inv.erase(current, inv.end()); 

Solo modificar el valor al que apunta un iterador no invalida ningún otro iterador, por lo que podemos hacerlo sin tener que preocuparnos. En realidad, std::remove_if (al menos la implementación de gcc) hace algo muy similar (usando un loop clásico …), simplemente no borra nada y no borra.

Tenga en cuenta, sin embargo, que esto no es seguro para subprocesos (!); Sin embargo, esto también se aplica a algunas de las otras soluciones anteriores …

Voy a mostrar con el ejemplo, el siguiente ejemplo eliminar elementos extraños del vector:

 void test_del_vector(){ std::vector vecInt{0, 1, 2, 3, 4, 5}; //method 1 for(auto it = vecInt.begin();it != vecInt.end();){ if(*it % 2){// remove all the odds it = vecInt.erase(it); } else{ ++it; } } // output all the remaining elements for(auto const& it:vecInt)std::cout< 

salida aw a continuación:

 024 024 024 

Tenga en cuenta que el método erase devolverá el siguiente iterador del iterador pasado.

Desde aquí , podemos usar un método más generate:

 template void erase_where(Container& c, F&& f) { c.erase(std::remove_if(c.begin(), c.end(),std::forward(f)), c.end()); } void test_del_vector(){ std::vector vecInt{0, 1, 2, 3, 4, 5}; //method 4 auto is_odd = [](int x){return x % 2;}; erase_where(vecInt, is_odd); // output all the remaining elements for(auto const& it:vecInt)std::cout< 

Vea aquí para ver cómo usar std::remove_if . https://en.cppreference.com/w/cpp/algorithm/remove

Una solución mucho más elegante sería cambiar a std::list (suponiendo que no necesita acceso aleatorio rápido).

 list widgets ; // create and use this.. 

Luego puede eliminar con .remove_if y un functor C ++ en una línea:

 widgets.remove_if( []( Widget*w ){ return w->isExpired() ; } ) ; 

Así que aquí estoy escribiendo un functor que acepta un argumento (el Widget* ). El valor de retorno es la condición para eliminar un Widget* de la lista.

Encuentro que esta syntax es aceptable. No creo que alguna vez use remove_if para std :: vectores – hay tanto inv.begin() y inv.end() ruido que probablemente sea mejor usar una eliminación basada en índices enteros o simplemente una simple eliminación regular basada en un iterador (como se muestra a continuación). Pero en realidad no deberías estar eliminando de la mitad de un std::vector mucho de todos modos, así que se recomienda cambiar a una list para este caso de eliminación frecuente de la mitad de la lista.

Sin embargo, tenga en cuenta que no tuve la oportunidad de llamar a delete en los Widget* que se eliminaron. Para hacer eso, se vería así:

 widgets.remove_if( []( Widget*w ){ bool exp = w->isExpired() ; if( exp ) delete w ; // delete the widget if it was expired return exp ; // remove from widgets list if it was expired } ) ; 

También podría usar un bucle regular basado en iteradores como ese:

 // NO INCREMENT v for( list::iterator iter = widgets.begin() ; iter != widgets.end() ; ) { if( (*iter)->isExpired() ) { delete( *iter ) ; iter = widgets.erase( iter ) ; // _advances_ iter, so this loop is not infinite } else ++iter ; } 

Si no le gusta la longitud de for( list::iterator iter = widgets.begin() ; ... , puede usar

 for( auto iter = widgets.begin() ; ... 

Creo que haría lo siguiente …

 for (auto itr = inv.begin(); itr != inv.end();) { // Do some stuff if (OK, I decided I need to remove this object from 'inv') itr = inv.erase(itr); else ++itr; }