Necesito pasar por un conjunto y eliminar los elementos que cumplen con un criterio predefinido.Eliminación de elementos del conjunto de STL al iterar
Este es el código de prueba que escribí:
#include <set>
#include <algorithm>
void printElement(int value) {
std::cout << value << " ";
}
int main() {
int initNum[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::set<int> numbers(initNum, initNum + 10);
// print '0 1 2 3 4 5 6 7 8 9'
std::for_each(numbers.begin(), numbers.end(), printElement);
std::set<int>::iterator it = numbers.begin();
// iterate through the set and erase all even numbers
for (; it != numbers.end(); ++it) {
int n = *it;
if (n % 2 == 0) {
// wouldn't invalidate the iterator?
numbers.erase(it);
}
}
// print '1 3 5 7 9'
std::for_each(numbers.begin(), numbers.end(), printElement);
return 0;
}
Al principio, pensé que borrar un elemento del conjunto, mientras que la iteración a través de él invalidaría el iterador, y el incremento en el bucle for tendría indefinido comportamiento. Aunque, ejecuté este código de prueba y todo salió bien, y no puedo explicar por qué.
Mi pregunta es: ¿Es este el comportamiento definido para los conjuntos estándar o es esta implementación específica? Estoy usando gcc 4.3.3 en ubuntu 10.04 (versión de 32 bits), por cierto.
Gracias!
solución propuesta:
Es esta una manera correcta para iterar y borrar elementos de la serie?
while(it != numbers.end()) {
int n = *it;
if (n % 2 == 0) {
// post-increment operator returns a copy, then increment
numbers.erase(it++);
} else {
// pre-increment operator increments, then return
++it;
}
}
Editar: PREFERENTE SOLUCIÓN
me llegó en torno a una solución que parece más elegante para mí, a pesar de que hace exactamente lo mismo.
while(it != numbers.end()) {
// copy the current iterator then increment it
std::set<int>::iterator current = it++;
int n = *current;
if (n % 2 == 0) {
// don't invalidate iterator it, because it is already
// pointing to the next element
numbers.erase(current);
}
}
Si hay varias condiciones de prueba dentro del tiempo, cada una de ellas debe incrementar el iterador. Me gusta más este código porque el iterador se incrementa solo en un lugar, haciendo que el código sea menos propenso a errores y más legible.
preguntado y contestado: http : //stackoverflow.com/questions/263945/what-happens-if-you-call-erase-on-a-map-element-while-iterating-from-begin-to-e/263958 –
En realidad, leí esto pregunta (y otros) antes de preguntar el mío, pero ya que estaban relacionados a otros contenedores de STL y dado que mi prueba inicial aparentemente funcionó, pensé que había alguna diferencia entre ellos. Solo después de la respuesta de Matt, pensé en usar valgrind. Aunque, prefiero mi NUEVA solución sobre las demás porque reduce las posibilidades de errores al incrementar el iterador en un solo lugar. ¡Gracias a todos por la ayuda! – pedromanoel
@pedromanoel '++ it' debe ser algo más eficiente que' it ++ 'porque no requiere el uso de una copia temporal invisible del iterador. La versión de Kornel durante más tiempo garantiza que los elementos no filtrados se iteren de la manera más eficiente. – Alnitak