¿Se pueden eliminar elementos de una lista std :: mientras se itera a través de ella?

Tengo un código que se ve así:

for (std::list::iterator i=items.begin();i!=items.end();i++) { bool isActive = (*i)->update(); //if (!isActive) // items.remove(*i); //else other_code_involving(*i); } items.remove_if(CheckItemNotActive); 

Me gustaría eliminar elementos inactivos inmediatamente después de actualizarlos, para evitar volver a recorrer la lista. Pero si agrego las líneas comentadas, aparece un error cuando llego a i++ : “List iterator no incrementable”. Probé algunas alternativas que no se incrementaron en la instrucción for, pero no pude hacer que funcionara nada.

¿Cuál es la mejor manera de eliminar elementos mientras recorre una std :: list?

Primero debe boost el iterador (con i ++) y luego eliminar el elemento anterior (p. Ej., Utilizando el valor devuelto de i ++). Puede cambiar el código a un ciclo while como ese:

 std::list::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; } } 

Quieres hacer:

 i= items.erase(i); 

Eso actualizará correctamente el iterador para que apunte a la ubicación después del iterador que eliminó.

Debes hacer la combinación de la respuesta de Kristo y MSN:

 // Note: Using the pre-increment operator is preferred for iterators because // there can be a performance gain. // // Note: As long as you are iterating from beginning to end, without inserting // along the way you can safely save end once; otherwise get it at the // top of each loop. std::list< item * >::iterator iter = items.begin(); std::list< item * >::iterator end = items.end(); while (iter != items.end()) { item * pItem = *iter; if (pItem->update() == true) { other_code_involving(pItem); ++iter; } else { // BTW, who is deleting pItem, aka (*iter)? iter = items.erase(iter); } } 

Por supuesto, la cosa más eficiente y estupenda de SuperCool® STL sería algo como esto:

 // This implementation of update executes other_code_involving(Item *) if // this instance needs updating. // // This method returns true if this still needs future updates. // bool Item::update(void) { if (m_needsUpdates == true) { m_needsUpdates = other_code_involving(this); } return (m_needsUpdates); } // This call does everything the previous loop did!!! (Including the fact // that it isn't deleting the items that are erased!) items.remove_if(std::not1(std::mem_fun(&Item::update))); 

Use el algoritmo std :: remove_if.

Editar: Trabajar con colecciones debería ser como: 1. preparar la colección. 2. proceso de recolección.

La vida será más fácil si no mezclas estos pasos.

  1. std :: remove_if. o list :: remove_if (si sabes que trabajas con list y no con TCollection)
  2. std :: for_each

La alternativa para la versión de bucle para la respuesta de Kristo.

Perderá algo de eficiencia, irá hacia atrás y luego hacia adelante nuevamente al eliminar, pero a cambio del incremento adicional del iterador puede hacer que el iterador se declare en el scope del bucle y el código se vea un poco más limpio. Qué elegir depende de las prioridades del momento.

La respuesta fue totalmente fuera de tiempo, lo sé …

 typedef std::list::iterator item_iterator; for(item_iterator i = items.begin(); i != items.end(); ++i) { bool isActive = (*i)->update(); if (!isActive) { items.erase(i--); } else { other_code_involving(*i); } } 

Aquí hay un ejemplo que utiliza un ciclo for que itera la lista e incrementa o revalida el iterador en caso de que se elimine un elemento durante el recorrido de la lista.

 for(auto i = items.begin(); i != items.end();) { if(bool isActive = (*i)->update()) { other_code_involving(*i); ++i; } else { i = items.erase(i); } } items.remove_if(CheckItemNotActive); 

La eliminación invalida solo los iteradores que apuntan a los elementos que se eliminan.

Entonces, en este caso después de eliminar * i, i se invalida y no se puede incrementar.

Lo que puedes hacer es guardar primero el iterador del elemento que se va a eliminar, luego incrementar el iterador y luego eliminar el guardado.

Puedes escribir

 std::list::iterator i = items.begin(); while (i != items.end()) { bool isActive = (*i)->update(); if (!isActive) { i = items.erase(i); } else { other_code_involving(*i); i++; } } 

Puede escribir un código equivalente con std::list::remove_if , que es menos detallado y más explícito

 items.remove_if([] (item*i) { bool isActive = (*i)->update(); if (!isActive) return true; other_code_involving(*i); return false; }); 

La expresión std::vector::erase std::remove_if debe usar cuando items es un vector en lugar de una lista para mantener la competencia en O (n), o en caso de que escriba código genérico y los artículos pueden ser un contenedor sin validez. forma de borrar elementos individuales (como un vector)

 items.erase(std::remove_if(begin(items), end(items), [] (item*i) { bool isActive = (*i)->update(); if (!isActive) return true; other_code_involving(*i); return false; })); 

Si piensa en std::list como en una cola, puede quitar la cola y poner en cola todos los elementos que desea conservar, pero solo dequeue (y no enqueue) el elemento que desea eliminar. Aquí hay un ejemplo en el que quiero eliminar 5 de una lista que contiene los números del 1 al 10 …

 std::list myList; int size = myList.size(); // The size needs to be saved to iterate through the whole thing for (int i = 0; i < size; ++i) { int val = myList.back() myList.pop_back() // dequeue if (val != 5) { myList.push_front(val) // enqueue if not 5 } } 

myList ahora solo tendrá los números 1-4 y 6-10.

Creo que tienes un error allí, código de esta manera:

 for (std::list::iterator itAudioChannel = audioChannels.begin(); itAudioChannel != audioChannels.end(); ) { CAudioChannel *audioChannel = *itAudioChannel; std::list::iterator itCurrentAudioChannel = itAudioChannel; itAudioChannel++; if (audioChannel->destroyMe) { audioChannels.erase(itCurrentAudioChannel); delete audioChannel; continue; } audioChannel->Mix(outBuffer, numSamples); }