Modification du contenu du vecteur dans BOOST_FOREACH
Question
C’est une question qui explique comment BOOST_FOREACH vérifie sa terminaison de boucle
cout << "Testing BOOST_FOREACH" << endl;
vector<int> numbers; numbers.reserve(8);
numbers.push_back(1); numbers.push_back(2); numbers.push_back(3);
cout << "capacity = " << numbers.capacity() << endl;
BOOST_FOREACH(int elem, numbers)
{
cout << elem << endl;
if (elem == 2) numbers.push_back(4);
}
cout << "capacity = " << numbers.capacity() << endl;
donne le résultat
Testing BOOST_FOREACH
capacity = 8
1
2
3
capacity = 8
Mais qu'en est-il du nombre 4 qui a été inséré au milieu de la boucle? Si je change le type en liste, le nouveau numéro inséré sera itéré. L'opération push_back de vecteur invalidera les pointeurs SI une réallocation est requise, mais cela ne se produit pas dans cet exemple. Je suppose donc que la question est de savoir pourquoi l’itérateur end () ne semble être évalué qu’une fois (avant la boucle) lorsqu’il utilise un vecteur, mais a une évaluation plus dynamique lorsqu’il utilise une liste.
La solution
Sous les couvertures, BOOST_FOREACH utilise itérateurs pour parcourir l'élément séquence. Avant que la boucle soit exécutée, l'itérateur final est mis en cache dans un local variable. Ceci s'appelle le levage, et c'est une optimisation importante. Il suppose cependant que la fin L'itérateur de la séquence est stable. Il est généralement, mais si nous modifions la séquence en ajoutant ou en supprimant éléments pendant que nous itérons sur on peut finir par se hisser sur notre propre pétard.
http://www.boost.org/ doc / libs / 1_40_0 / doc / html / foreach / pitfalls.html
Si vous ne voulez pas que l'itérateur end () change, utilisez redimensionner le vecteur plutôt que réserver.
http://www.cplusplus.com/reference/stl/vector/resize /
Notez qu'alors vous ne voudriez pas push_back mais utilisez l'opérateur [] à la place. Mais faites attention à ne pas sortir du terrain.
Autres conseils
La question soulevée dans les commentaires était de savoir pourquoi l'exécution de débogage de Microsoft levait une assertion lors d'une itération sur le vecteur mais pas sur la liste. La raison en est que insert
est défini différemment pour liste
et vector
(notez que push_back
est simplement un insérez
à la fin de la séquence).
Conformément à la norme C ++ (ISO / IEC 14882: 2003 23.2.4.3, modificateurs de vecteur ):
[à l'insertion], si aucune réallocation ne se produit, tous les itérateurs et références précédant le point d'insertion restent valides.
(23.2.2.3, modificateurs de liste ):
[insert] n'affecte pas la validité des itérateurs et des références.
Ainsi, si vous utilisez push_back
(et que vous êtes sûr que cela ne provoquera pas de réallocation), l'un ou l'autre conteneur peut continuer à utiliser votre itérateur pour effectuer une itération sur le reste de la séquence.
Cependant, dans le cas du vecteur, il est un comportement non défini d'utiliser l'itérateur end
que vous avez obtenu avant le push_back
.
Ceci est une réponse détournée à la question; c'est une réponse directe à la discussion dans les commentaires de la question.
foreach de boost se terminera s'il est itérateur == nombres.end ()
Attention, appeler Push_back peut / va invalider tous les itérateurs actuels.