Necesito revisar un conjunto y eliminar elementos que cumplan con un criterio predefinido.
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 lo iteraba invalidaría el iterador, y el incremento en el bucle for tendría un comportamiento indefinido. Aunque ejecuté este código de prueba y todo salió bien, y no puedo explicar por qué.
Mi pregunta: ¿Es este el comportamiento definido para los conjuntos estándar o esta implementación es específica? Por cierto, estoy usando gcc 4.3.3 en ubuntu 10.04 (versión de 32 bits).
¡Gracias!
Solución propuesta:
¿Es esta una forma correcta de iterar y borrar elementos del conjunto?
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: SOLUCIÓN PREFERIDA
Encontré una solución que me parece más elegante, 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 , lo que hace que el código sea menos propenso a errores y más legible.
++it
debería ser algo más eficiente que it++
porque no requiere el uso de una copia temporal invisible del iterador. La versión de Kornel, si bien es más larga, garantiza que los elementos no filtrados se repitan de la manera más eficiente.