Je dois parcourir un ensemble et 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 que le fait d'effacer un élément de l'ensemble tout en l'itérant invaliderait l'itérateur, et que l'incrément de la boucle for aurait un comportement indéfini. Pourtant, j'ai exécuté ce code de test et tout s'est bien passé, sans que je puisse expliquer pourquoi.
Ma question : S'agit-il du comportement défini pour les ensembles std ou d'une implémentation spécifique ? Au fait, j'utilise gcc 4.3.3 sur ubuntu 10.04 (version 32 bits).
Gracias.
Solution proposée :
Est-ce une façon 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 à l'intérieur du while, chacune d'entre elles doit incrémenter l'itérateur. J'aime mieux ce code car l'itérateur est incrémenté seulement à un endroit ce qui rend le code moins sujet aux erreurs et plus lisible.
1 votes
Questions et réponses : stackoverflow.com/questions/263945/
3 votes
En fait, j'ai lu cette question (et d'autres) avant de poser la mienne, mais comme elles concernaient d'autres conteneurs STL et que mon test initial avait apparemment fonctionné, je pensais qu'il y avait une différence entre elles. Ce n'est qu'après la réponse de Matt que j'ai pensé à utiliser valgrind. Malgré tout, je préfère ma NOUVELLE solution aux autres car elle réduit les risques d'erreurs en incrémentant l'itérateur à un seul endroit. Merci à tous pour votre aide !
1 votes
@pedromanoel
++it
devrait être un peu plus efficace queit++
car elle ne nécessite pas l'utilisation d'une copie temporaire invisible de l'itérateur. La version de Kornel assure que les éléments non filtrés sont itérés le plus efficacement possible.0 votes
@Alnitak Je n'ai pas pensé à cela, mais je pense que la différence de performance ne serait pas si grande. La copie est créée dans sa version aussi, mais seulement pour les éléments qui correspondent. Donc le degré d'optimisation dépend totalement de la structure de l'ensemble. Pendant un certain temps, j'ai pré-optimisé le code, nuisant ainsi à la lisibilité et à la vitesse de codage... Je ferais donc des tests avant d'utiliser l'autre méthode.
1 votes
Duplicata possible de Peut-on supprimer des éléments d'une std::list tout en la parcourant ?
0 votes
Le problème original que vous essayez de résoudre est-il simplement de "retirer les éléments (d'un ensemble) qui répondent à un critère prédéfini" ? Parce que vous n'avez même pas besoin d'itération pour cela, ce qui rendrait les questions plus spécifiques sur l'effacement tout en itérant redondantes. Mais si vous ont pour effacer tout en itérant, alors je ne peux pas vous aider :)
0 votes
Wow... J'ai posé cette question il y a si longtemps que je ne me souviens plus de mon problème initial ! Que voulez-vous dire par "je n'ai pas besoin d'itération dans ce cas" ?
0 votes
Désolé, ça ne fait rien, j'ai eu un malentendu à propos du STL. :)
0 votes
J'ai des difficultés avec votre solution préférée, je semble obtenir une boucle infinie. J'utilise un
deque
plutôt qu'unset
cependant le reste de mon code est un cas de test minimum pour la méthode que vous proposez...