Puoi rimuovere elementi da una lista std :: iterando attraverso di essa?

Ho il codice che assomiglia a questo:

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); 

Mi piacerebbe rimuovere gli elementi inattivi immediatamente dopo l’aggiornamento, inorder per evitare di tornare all’elenco. Ma se aggiungo le righe commentate, ottengo un errore quando arrivo a i++ : “Elenco iteratore non incrementabile”. Ho provato alcune alternative che non sono aumentate nell’istruzione for, ma non ho potuto ottenere nulla per funzionare.

Qual è il modo migliore per rimuovere gli oggetti mentre stai camminando su una lista std :: list?

È necessario incrementare prima l’iteratore (con i ++) e quindi rimuovere l’elemento precedente (ad esempio, utilizzando il valore restituito da i ++). Puoi cambiare il codice in un ciclo while in questo modo:

 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; } } 

Vuoi fare:

 i= items.erase(i); 

Ciò aggiornerà correttamente l’iteratore in modo che punti alla posizione dopo l’iteratore rimosso.

Devi fare la combinazione della risposta di Kristo e di 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); } } 

Ovviamente, la cosa più efficiente e superCool® STL savy sarebbe qualcosa del genere:

 // 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))); 

Utilizza l’algoritmo std :: remove_if.

Modifica: lavorare con le raccolte dovrebbe essere come: 1. preparare la raccolta. 2. elaborare la raccolta.

La vita sarà più facile se non mischierai questi passaggi.

  1. std :: remove_if. o lista :: remove_if (se sai che lavori con la lista e non con la TCollection)
  2. std :: for_each

L’alternativa per la versione loop alla risposta di Kristo.

Si perde un po ‘di efficienza, si va indietro e poi di nuovo in fase di cancellazione, ma in cambio dell’incremento dell’iteratore extra si può avere l’iteratore dichiarato nell’ambito del ciclo e il codice sembra un po’ più pulito. Cosa scegliere dipende dalle priorità del momento.

La risposta era totalmente fuori dal tempo, lo so …

 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); } } 

Ecco un esempio che utilizza un ciclo for che itera l’elenco e incrementa o riconvalida l’iteratore nel caso in cui un elemento venga rimosso durante l’attraversamento della 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 rimozione invalida solo gli iteratori che puntano agli elementi che vengono rimossi.

Quindi in questo caso dopo aver rimosso * i, i viene invalidato e non è ansible eseguire incrementi su di esso.

Quello che puoi fare è prima salvare l’iteratore dell’elemento che deve essere rimosso, quindi incrementare l’iteratore e quindi rimuovere quello salvato.

Tu puoi scrivere

 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++; } } 

È ansible scrivere codice equivalente con std::list::remove_if , che è meno dettagliato e più esplicito

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

L’idioma std::vector::erase std::remove_if deve essere usato quando gli elementi sono un vettore invece di un elenco per mantenere la complicazione su O (n) – o nel caso in cui si scriva codice generico e gli oggetti potrebbero essere un contenitore senza effetto modo per cancellare singoli oggetti (come un vettore)

 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; })); 

Se si pensa a std::list come una coda, è ansible deselezionare e accodare tutti gli elementi che si desidera conservare, ma solo deselezionare (e non accodare) l’elemento che si desidera rimuovere. Ecco un esempio in cui voglio rimuovere 5 da una lista contenente i numeri 1-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 ora avrà solo i numeri 1-4 e 6-10.

Penso che tu abbia un bug lì, codice in questo modo:

 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); }