Quelles sont les règles d'invalidation de l'itérateur pour les conteneurs C++?
De préférence dans un format de liste récapitulative.
(Remarque: il s'agit d'une entrée dans FAQ C++ de Stack Overflow . Si vous souhaitez critiquer l'idée de fournir un FAQ dans ce formulaire, alors l'affichage sur meta qui a commencé tout cela serait l'endroit pour le faire. Les réponses à cette question sont suivies dans le salle de discussion C++ , où le FAQ idée a commencé à la base, donc votre réponse sera très probablement lue par ceux qui en ont eu l'idée.)
C++ 17 (Toutes les références proviennent du projet de travail final de CPP17 - n4659 )
Conteneurs de séquence
vector
: Les fonctions insert
, emplace_back
, emplace
, Push_back
provoquent une réallocation si la nouvelle taille est supérieure à l'ancienne capacité. La réallocation invalide toutes les références, les pointeurs et les itérateurs se rapportant aux éléments de la séquence. Si aucune réaffectation ne se produit, tous les itérateurs et références précédant le point d'insertion restent valides. [26.3.11.5/1]
En ce qui concerne la fonction reserve
, la réallocation invalide toutes les références, pointeurs et itérateurs se rapportant aux éléments de la séquence. Aucune réallocation ne doit avoir lieu pendant les insertions qui se produisent après un appel à reserve()
jusqu'au moment où une insertion rendrait la taille du vecteur supérieure à la valeur de capacity()
. [26.3.11.3/6]
deque
: Une insertion au milieu du deque invalide tous les itérateurs et les références aux éléments du deque. Une insertion à l'une des extrémités du deque annule tous les itérateurs du deque, mais n'a aucun effet sur la validité des références aux éléments du deque. [26.3.8.4/1]
list
: N'affecte pas la validité des itérateurs et des références. Si une exception est levée, il n'y a aucun effet. [26.3.10.4/1].
Les fonctions insert
, emplace_front
, emplace_back
, emplace
, Push_front
, Push_back
sont traitées dans cette règle.
forward_list
: aucune des surcharges de insert_after
n'affectera la validité des itérateurs et des références [26.3.9.5/1]
array
: En règle générale , les itérateurs d'un tableau ne sont jamais invalidés pendant toute la durée de vie du tableau. Il convient toutefois de noter que lors de l’échange, l’itérateur continuera de pointer vers le même élément de tableau et modifiera donc sa valeur.
Conteneurs associatifs
All Associative Containers
: Les membres insert
et emplace
ne doivent pas affecter la validité des itérateurs ni des références au conteneur [26.2.6/9]Conteneurs associatifs non ordonnés
All Unordered Associative Containers
: Rehase invalide les itérateurs, modifie l'ordre des éléments et modifie les éléments de paniers qui apparaissent dans, mais n'invalide pas les pointeurs ni les références aux éléments. [26.2.7/9]
Les membres insert
et emplace
n'affectent pas la validité des références aux éléments de conteneur, mais peuvent invalider tous les itérateurs du conteneur. [26.2.7/14]
Les membres insert
et emplace
n'affecteront pas la validité des itérateurs si (N+n) <= z * B
, où N
est le nombre d'éléments dans le conteneur avant l'insertion. operation, n
est le nombre d'éléments insérés, B
est le nombre de compartiments du conteneur et z
est le facteur de charge maximum du conteneur. [26.2.7/15]
All Unordered Associative Containers
: En cas d'opération de fusion (par exemple, a.merge(a2)
), les itérateurs faisant référence aux éléments transférés et tous les itérateurs faisant référence à a
seront invalidés, mais les itérateurs des éléments restant dans a2
seront reste valide. (Tableau 91 - Exigences relatives aux conteneurs associatifs non ordonnés)
Adaptateurs de conteneur
stack
: hérité du conteneur sous-jacentqueue
: hérité du conteneur sous-jacentpriority_queue
: hérité du conteneur sous-jacentConteneurs de séquence
vector
: Les fonctions erase
et pop_back
invalident les itérateurs et les références au moment ou après le point de l'effacement. [26.3.11.5/3]
deque
: une opération d'effacement qui efface le dernier élément d'un deque
invalide uniquement l'itérateur passé-the-end et tous les itérateurs et les références aux éléments effacés. Une opération d'effacement qui efface le premier élément d'un deque
mais pas le dernier élément n'invalide que les itérateurs et les références aux éléments effacés. Une opération d'effacement qui n'efface ni le premier élément ni le dernier élément d'un deque
invalide l'itérateur passé-fin et tous les itérateurs, ainsi que les références à tous les éléments de deque
. [Remarque: pop_front
et pop_back
sont des opérations d'effacement. —Fin note] [26.3.8.4/4]
list
: Invalide uniquement les itérateurs et les références aux éléments effacés. [26.3.10.4/3]. Ceci s'applique aux fonctions erase
, pop_front
, pop_back
, clear
.remove
et remove_if
fonctions membres: efface tous les éléments de la liste référés par un itérateur de liste i
pour lesquels les conditions suivantes sont remplies: *i == value
, pred(*i) != false
. Invalide uniquement les itérateurs et les références aux éléments effacés [26.3.10.5/15].unique
fonction membre - Efface tout, sauf le premier élément, de chaque groupe consécutif d'éléments égaux auxquels se réfère l'itérateur i
compris entre [first + 1, last)
pour lequel *i == *(i-1)
(pour le La version de unique sans arguments) ou pred(*i, *(i - 1))
(pour la version de unique avec un argument de prédicat) est maintenue. Invalide uniquement les itérateurs et les références aux éléments effacés. [26.3.10.5/19]
forward_list
: erase_after
n'invalidera que les itérateurs et les références aux éléments effacés. [26.3.9.5/1].remove
and remove_if
fonctions membres - Efface tous les éléments de la liste référés par un itérateur de liste i pour lesquels les conditions suivantes sont vérifiées: *i == value
(pour remove()
), pred(*i)
est vrai (pour remove_if()
). Invalide uniquement les itérateurs et les références aux éléments effacés. [26.3.9.6/12].unique
fonction membre - Efface tout sauf le premier élément de chaque groupe consécutif d'éléments égaux auquel se réfère l'itérateur i dans la plage [premier + 1, dernier] pour lequel *i == *(i-1)
(pour la version avec aucun argument) ou pred(*i, *(i - 1))
(pour la version avec un argument de prédicat) est valide. Invalide uniquement les itérateurs et les références aux éléments effacés. [26.3.9.6/16]
All Sequence Containers
: clear
invalide toutes les références, pointeurs et itérateurs se rapportant aux éléments d'un et peut invalider l'itérateur passé-fin (Tableau 87 - Exigences du conteneur de séquence). Mais pour forward_list
, clear
n'invalide pas les itérateurs passés. [26.3.9.5/32]
All Sequence Containers
: assign
invalide toutes les références, pointeurs et itérateurs se rapportant aux éléments du conteneur. Pour vector
et deque
, invalide également l'itérateur passé-la-fin. (Tableau 87 - Exigences relatives aux conteneurs de séquence)
Conteneurs associatifs
All Associative Containers
: Les membres erase
n'invalident que les itérateurs et les références aux éléments effacés [26.2.6/9]
All Associative Containers
: Les membres extract
n'invalident que les itérateurs de l'élément supprimé; les pointeurs et les références à l'élément supprimé restent valables [26.2.6/10]
Adaptateurs de conteneur
stack
: hérité du conteneur sous-jacentqueue
: hérité du conteneur sous-jacentpriority_queue
: hérité du conteneur sous-jacentExigences générales relatives aux conteneurs concernant l'invalidation de l'itérateur:
Sauf indication contraire (soit explicitement, soit en définissant une fonction en fonction d'autres fonctions), invoquer une fonction membre du conteneur ou transmettre un conteneur en tant qu'argument à une fonction de bibliothèque ne doit pas invalider les itérateurs ou modifier les valeurs des objets de ce conteneur. . [26.2.1/12]
aucune fonction swap()
n'invalide les références, les pointeurs ou les itérateurs faisant référence aux éléments des conteneurs en cours d'échange. [Remarque: l'itérateur end () ne fait référence à aucun élément et peut donc être invalidé. —Fin note] [26.2.1/(11.6)]
À titre d'exemples des exigences ci-dessus:
transform
algorithme: les fonctions op
et binary_op
ne doivent pas invalider les itérateurs ou les sous-plages, ni modifier les éléments compris dans les plages [28.6.4/1]
accumulate
algorithme: dans la plage [premier, dernier], binary_op
ne modifiera pas les éléments ni n'invalidera les itérateurs ou les sous-plages [29.8.2/1]
reduce
algorithme: binary_op ne doit ni invalider les itérateurs ou les sous-plages, ni modifier les éléments compris dans la plage [premier, dernier]. [29.8.3/5]
etc...
C++ 03 (Source: Règles d'invalidation d'Iterator (C++ 03) )
Conteneurs de séquence
vector
: tous les itérateurs et références antérieurs au point d'insertion ne sont pas affectés, sauf si la nouvelle taille du conteneur est supérieure à la capacité précédente (dans ce cas, tous les itérateurs et références sont invalidés) [23.2.4.3/1]deque
: tous les itérateurs et références sont invalidés, sauf si le membre inséré se trouve à une extrémité (avant ou arrière) du deque (auquel cas tous les itérateurs sont invalidés, mais les références aux éléments ne sont pas affectées) [23.2.1.3/ 1]list
: tous les itérateurs et références non affectés [23.2.2.3/1]Conteneurs associatifs
[multi]{set,map}
: tous les itérateurs et références non affectés [23.1.2/8]Adaptateurs de conteneur
stack
: hérité du conteneur sous-jacentqueue
: hérité du conteneur sous-jacentpriority_queue
: hérité du conteneur sous-jacentConteneurs de séquence
vector
: chaque itérateur et référence après le point d'effacement est invalidé [23.2.4.3/3]deque
: tous les itérateurs et références sont invalidés, sauf si les membres effacés se trouvent à une extrémité (avant ou arrière) de la deque (auquel cas seuls les itérateurs et les références aux membres effacés sont invalidés) [23.2.1.3/4 ]list
: seuls les itérateurs et les références à l'élément effacé sont invalidés [23.2.2.3/3]Conteneurs associatifs
[multi]{set,map}
: seuls les itérateurs et les références aux éléments effacés sont invalidés [23.1.2/8]Adaptateurs de conteneur
stack
: hérité du conteneur sous-jacentqueue
: hérité du conteneur sous-jacentpriority_queue
: hérité du conteneur sous-jacentvector
: selon insertion/effacement [23.2.4.2/6]deque
: selon insertion/effacement [23.2.1.2/1]list
: selon insertion/effacement [23.2.2.2/1]Sauf indication contraire (soit explicitement, soit en définissant une fonction en fonction d'autres fonctions), invoquer une fonction membre du conteneur ou transmettre un conteneur en tant qu'argument à Une fonction de bibliothèque ne doit pas invalider les itérateurs ni modifier les valeurs des objets de ce conteneur. [23.1/11]
En C++ 2003, il n'est pas clair si les itérateurs "de fin" sont soumis aux règles ci-dessus ; de toute façon, vous devez supposer qu’ils le sont (comme c’est le cas en pratique).
Les règles d'invalidation des pointeurs sont les mêmes que les règles d'invalidation des références.
C++ 11 (Source: Règles d'invalidation d'Iterator (C++ 0x) )
Conteneurs de séquence
vector
: tous les itérateurs et les références antérieurs au point d'insertion ne sont pas affectés, sauf si la nouvelle taille du conteneur est supérieure à la capacité précédente (auquel cas tous les itérateurs et les références sont invalidés) [23.3.6.5/1]deque
: tous les itérateurs et références sont invalidés, sauf si le membre inséré se trouve à une extrémité (avant ou arrière) du deque (auquel cas tous les itérateurs sont invalidés, mais les références aux éléments ne sont pas affectées) [23.3.3.4/1]list
: tous les itérateurs et références non affectés [23.3.5.4/1]forward_list
: tous les itérateurs et références non affectés (s'applique à insert_after
) [23.3.4.5/1]array
: (n/a) Conteneurs associatifs
[multi]{set,map}
: tous les itérateurs et références non affectés [23.2.4/9]Conteneurs associatifs non triés
unordered_[multi]{set,map}
: tous les itérateurs sont invalidés en cas de ré-accentuation, mais référence non affectée [23.2.5/8]. Le rehashing ne se produit pas si l'insertion n'entraîne pas que la taille du conteneur dépasse z * B
où z
est le facteur de charge maximal et B
, le nombre actuel de compartiments. [23.2.5/14]Adaptateurs de conteneur
stack
: hérité du conteneur sous-jacentqueue
: hérité du conteneur sous-jacentpriority_queue
: hérité du conteneur sous-jacentConteneurs de séquence
vector
: chaque itérateur et référence au ou après le point d'effacement est invalidé [23.3.6.5/3]deque
: effacer le dernier élément n'invalide que les itérateurs et les références aux éléments effacés et à l'itérateur précédent; effacer le premier élément n'invalide que les itérateurs et les références aux éléments effacés; l'effacement de tout autre élément invalide tous les itérateurs et les références (y compris l'itérateur précédent) [23.3.3.4/4]list
: seuls les itérateurs et les références à l'élément effacé sont invalidés [23.3.5.4/3]forward_list
: seuls les itérateurs et les références à l'élément effacé sont invalidés (s'applique à erase_after
) [23.3.4.5/1]array
: (n/a) Conteneurs associatifs
[multi]{set,map}
: seuls les itérateurs et les références aux éléments effacés sont invalidés [23.2.4/9]Conteneurs associatifs non ordonnés
unordered_[multi]{set,map}
: seuls les itérateurs et les références aux éléments effacés sont invalidés [23.2.5/13]Adaptateurs de conteneur
stack
: hérité du conteneur sous-jacentqueue
: hérité du conteneur sous-jacentpriority_queue
: hérité du conteneur sous-jacentvector
: as insert/erase [23.3.6.5/12]deque
: selon insert/erase [23.3.3.3/3]list
: selon insert/erase [23.3.5.3/1]forward_list
: selon insertion/effacement [23.3.4.5/25]array
: (n/a)Sauf indication contraire (soit explicitement, soit en définissant une fonction en fonction d'autres fonctions), invoquer une fonction membre du conteneur ou transmettre un conteneur en tant qu'argument à Une fonction de bibliothèque ne doit pas invalider les itérateurs ni modifier les valeurs des objets de ce conteneur. [23.2.1/11]
aucune fonction swap () n'invalide les références, pointeurs ou itérateurs faisant référence aux éléments des conteneurs en cours d'échange. [Remarque: L'itérateur end () ne fait référence à aucun élément. Par conséquent, il peut être invalidé . —Fin note] [23.2.1/10]
Autre que la mise en garde ci-dessus concernant swap()
, il n'est pas clair si les itérateurs "de fin" sont soumis aux règles par conteneur énumérées ci-dessus ; vous devriez supposer, de toute façon, qu'ils le sont.
vector
et tous les conteneurs associatifs non ordonnés prennent en charge reserve(n)
, ce qui garantit qu'aucun redimensionnement automatique ne se produira au moins jusqu'à ce que la taille du conteneur atteigne n
. Des précautions doivent être prises avec des conteneurs associatifs non ordonnés , car une future proposition permettra de spécifier un facteur de charge minimum, ce qui permettrait de redéfinir le redhashing sur insert
après suffisamment d'opérations erase
pour réduire la taille du conteneur. en dessous du minimum; la garantie doit être considérée comme potentiellement nulle après un erase
.
Il est probablement utile d’ajouter qu’un itérateur d’insert de tout type (std::back_insert_iterator
, std::front_insert_iterator
, std::insert_iterator
) reste garanti tant que toutes les insertions sont effectuées via cet itérateur et qu’aucun autre un événement invalidant l'itérateur se produit.
Par exemple, lorsque vous effectuez une série d'opérations d'insertion dans un std::vector
à l'aide de std::insert_iterator
, il est tout à fait possible que ces insertions déclenchent une réallocation de vecteur, ce qui invalidera tous les itérateurs qui "pointent" sur ce vecteur. . Cependant, il est garanti que l’itérateur d’insert en question reste valide, c’est-à-dire que vous pouvez continuer en toute sécurité la séquence d’insertions. Inutile de vous inquiéter de déclencher une réallocation de vecteur.
Ceci, encore une fois, ne s'applique qu'aux insertions effectuées à travers l'itérateur d'insert lui-même. Si un événement invalidant un itérateur est déclenché par une action indépendante sur le conteneur, l'itérateur d'insertion est également invalidé conformément aux règles générales.
Par exemple, ce code
std::vector<int> v(10);
std::vector<int>::iterator it = v.begin() + 5;
std::insert_iterator<std::vector<int> > it_ins(v, it);
for (unsigned n = 20; n > 0; --n)
*it_ins++ = Rand();
est garanti pour effectuer une séquence valide d'insertions dans le vecteur, même si le vecteur "décide" de réallouer quelque part au milieu de ce processus. Iterator it
deviendra évidemment invalide, mais it_ins
continuera à rester valide.
Étant donné que cette question suscite tant de votes et devient en quelque sorte une FAQ, il serait préférable d'écrire une réponse séparée pour mentionner une différence significative entre C++ 03 et C++ 11 concernant l'impact de l'insertion de std::vector
opération sur la validité des itérateurs et des références par rapport à reserve()
et capacity()
, que la réponse la plus votée n'a pas remarqué.
C++ 03:
La réallocation invalide toutes les références, les pointeurs et les itérateurs se rapportant aux éléments de la séquence. Il est garanti qu'aucune réaffectation n'a lieu pendant les insertions qui se produisent après un appel à reserve () jusqu'au moment où une insertion rendrait la taille du vecteur supérieure à la taille spécifiée dans le dernier appel à reserve ().
C++ 11:
La réallocation invalide toutes les références, les pointeurs et les itérateurs se rapportant aux éléments de la séquence. Il est garanti qu'aucune réallocation n'a lieu pendant les insertions qui se produisent après un appel à reserve () jusqu'au moment où une insertion rendrait la taille du vecteur supérieure à la valeur de capacity ().
Donc, en C++ 03, ce n'est pas "unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)
" comme mentionné dans l'autre réponse, mais plutôt "greater than the size specified in the most recent call to reserve()
". C++ 03 est différent de C++ 11. En C++ 03, une fois un insert()
, la taille du vecteur atteint la valeur spécifiée dans l'appel précédent reserve()
(qui pourrait bien être plus petit que le capacity()
actuel, car un reserve()
pourrait entraîner une capacity()
plus importante que celle demandée), toute insert()
ultérieure pourrait provoquer une réallocation et invalider tous les itérateurs et les références. Cela ne se produira pas dans C++ 11 et vous pourrez toujours faire confiance à capacity()
pour savoir avec certitude que la prochaine réallocation n'aura pas lieu avant que la taille ne dépasse capacity()
.
En conclusion, si vous travaillez avec un vecteur C++ 03 et que vous voulez vous assurer qu'une réallocation ne se produira pas lors de l'insertion, c'est la valeur de l'argument que vous avez précédemment passé à reserve()
que vous devez vérifier. la taille contre, pas la valeur de retour d'un appel à capacity()
, sinon vous pourriez vous surprendre à une réallocation " prématurée ".