J'ai besoin de parcourir un ensemble et de supprimer les éléments qui répondent à un critère prédéfini.
Voici le code de test que j'ai écrit:
#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;
}
Au début, je pensais qu'effacer un élément de l'ensemble lors de son itération invaliderait l'itérateur, et que l'incrémentation à la boucle for aurait un comportement indéfini. Même si, j'ai exécuté ce code de test et tout s'est bien passé, et je ne peux pas expliquer pourquoi.
Ma question: est-ce le comportement défini pour les ensembles std ou cette implémentation est-elle spécifique? J'utilise gcc 4.3.3 sur ubuntu 10.04 (version 32 bits), d'ailleurs.
Merci!
Solution proposée:
Est-ce une manière correcte d'itérer et d'effacer des éléments de l'ensemble?
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;
}
}
Edit: SOLUTION PRÉFÉRÉE
J'ai trouvé une solution qui me semble plus élégante, même si elle fait exactement la même chose.
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);
}
}
S'il y a plusieurs conditions de test dans le while, chacune d'elles doit incrémenter l'itérateur. J'aime mieux ce code car l'itérateur n'est incrémenté qu'à un seul endroit , ce qui rend le code moins sujet aux erreurs et plus lisible.
++it
devrait être un peu plus efficace que it++
parce qu'il ne nécessite pas l'utilisation d'une copie temporaire invisible de l'itérateur. La version Kornel, bien que plus longue, garantit que les éléments non filtrés sont itérés plus efficacement.