Est-ce que la norme interdit explicitement la modification d'un conteneur dans Plus spécifiquement, dans le cas de std :: for_ach code>?
std :: Liste code> Les itérateurs ne sont pas invalidés lorsque le la liste est modifiée. Ainsi, le code suivant fonctionne: P>
std::list<int> list;
list.push_front(5);
list.push_front(10);
auto it = list.end();
it--; // point to 5
std::for_each(list.begin(), list.end(), [&](int i){
/* the line below will remove the last element in the list;
* list will have only one element (the currently processed one);
* list.end() is not invalidated and we exit for_each() */
list.erase(it);
});
5 Réponses :
Oui, c'est légal. Il y a des iters constants si vous avez besoin de quelque chose qui est garanti pour ne pas modifier le conteneur sous-jacent. P>
Preuve? Si ce n'est pas un std :: Liste Code> ou je supprime un itérateur à l'élément actuellement traité, il va se bloquer.
Alors s'il vous plaît définir "légal". Je voulais dire: ce code est-il conforme à la norme ou fonctionne-t-il juste par accident?
Votre code est limite légal.
avec le code posté, il est légal par coïncidence. Si vous ajoutez un élément de plus à la liste, votre programme s'écrasera. P>
Essayez ce qui suit pour voir le problème: P>
int main() { std::list<int> list; list.push_front(5); list.push_front(10); list.push_front(12); auto it = list.end(); it--; // point to 5 std::for_each(list.begin(), list.end(), [&](int i){ /* the line below will remove the last element in the list; * list will have only one element (the currently processed one); * list.end() is not invalidated and we exit for_each() */ list.erase(it); }); }
Oui, cela effacera le même itérateur deux fois, ce qui n'est pas autorisé. Mais la question reste-t-elle conceptuellement autorisée à modifier le conteneur ou à déclencher une sorte de UB?
Il est ub d'appeler deux fois en utilisant le même itérateur car le premier appel à effacer rend l'itérateur invalide.
Oui, mais ce n'est pas ce que je demande. Supposons, il est assuré que l'itérateur n'est pas effacé deux fois. Est-ce toujours un UB?
@Kane, dans ce cas, je ne suivons pas votre question. Le point clé est que c'est que l'UB est d'utiliser un itérateur qui a été effacé. Si votre code n'utilise pas un itérateur après avoir été effacé, il n'est pas valide et ne devrait pas aboutir à UB.
Il ne s'agit pas seulement de mon code n'utilisant pas d'itérateur après une invalidité. Il s'agit également de std :: for_ach code> ne l'utilise pas après une invalidité.
@Kane, je m'impliquais ça. std :: for_ach code> appelez le dernier argument avec chaque valeur dans la plage
[premier, dernier) code>. Sauf si vous appelez Erase sur l'itérateur actuel que
std :: for_ach code> utilise, vous devriez être ok.
Est-ce que la norme interdit explicitement de modifier un conteneur dans
std :: for_ach code>? p> blockQuote>
La seule chose que je peux penser que ce code ne serait pas conforme standard, c'est que dans
[alg.foreach] code> nous avons p>
complexité: em> s'applique
f code> exactement
dernier - premier code> fois. p> blockQuote>
f code> être la fonction
pour_ach code> s'applique. sup> p>
Étant donné que la liste est modifiée et qu'un élément est supprimé, nous ne répondons plus à cette complexité. Je ne sais pas si cela le rend non conforme mais c'est la seule chose que je pouvais voir que cela ne vous permettrait pas de supprimer des éléments du conteneur lors de l'utilisation de
pour_ache p> p> p>
Est-ce que la norme interdit explicitement la modification d'un conteneur dans
std :: for_ach code>? p> blockQuote>
Non, pas explicitement fort>. Au moins pour toute définition raisonnable de "explicitement". La section sur
std :: for_ach code> (§25.2.4) ne dit rien du tout sur l'invalidation des itérateurs ou les effets secondaires causant la modification de la plage itératée. P>
Il y a cependant de nombreuses raisons fortes> implicites fortes> pourquoi votre code ne peut pas fonctionner. Considérez comment
std :: for_ach code> doit être mis en œuvre. C'est complètement générique et ne sait donc rien sur le
std :: list code> il fonctionne; Vous avez donné deux itérateurs à former une plage, ce qui signifie que
dernier code> est accessible depuis
premier code>. Comment
std :: for_ach code> sache que votre objet fonction a invalidé l'objet Itérateur local avec lequel il passe de
d'abord code> à
Dernier p >
Et même si la fonction savait que l'invalidation s'est produite, que devrait-il faire? Dans une boucle écrite à la main, vous obtiendrez le résultat de
effacer code> et continuez l'itération avec elle, mais
std :: for_ach code> ne peut pas faire cela, par définition. P >
Voici un article de référence ancien mais toujours valide: http: / /www.drdobbs.com/effective-standard-c-library-foreach-vs/184403769 P>
Il dit: P>
Par exemple, invalidez les itérateurs ou les séquences que la L'algorithme fonctionne avec est désastreuse dans tous les cas. Même la fonction objet fourni à destination de_ach doit obéir à cette règle de bon sens, même si La norme ne le dit pas. P> blockQuote>
Paraphraser quelques mots sages, j'ai une fois lu: p>
la norme C ++ est écrite par des êtres humains; Ce n'est pas toujours aussi parfait que possible. em> p>
25.2.4 / 2: P>
Effets: applique F au résultat de la déséroférance de chaque itérateur de la GAMME [PREMIER, DERNIER), à partir d'une première et d'une procédure au dernier - 1. P> blockQuote>
alors 25.2.4 / 4: P>
Complexité: Applique exactement f EXO exactement - première fois. P> blockQuote>
Je suis prêt à faire valoir que ces deux points interdisent explicitement la mutation du conteneur d'une manière qui change le nombre d'éléments. P>
qui dit, même ce n'est pas votre exemple échoue si la liste a même un élément de plus, ou si votre implémentation de la bibliothèque standard augmente l'itérateur avant d'appeler votre fonction (et je ne vois rien dans la norme qui interdit de telle une implémentation). P>
Cette question devrait être beaucoup plus perçue, vraiment ...