it-swarm.com.de

Iterator-Ungültigkeitsregeln

Was sind die Iterator-Ungültigkeitsregeln für C++ - Container?

Am besten in einer Zusammenfassungsliste.

(Hinweis: Dies ist ein Eintrag zu Stack Overflow's C++ FAQ . Wenn Sie die Idee, ein FAQ in diesem Formular bereitzustellen, kritisieren möchten, dann wäre das Posting auf Meta, das all dies gestartet hat der richtige Ort, um dies zu tun. Die Antworten auf diese Frage werden im C++ Chatroom überwacht, in dem die Idee FAQ an erster Stelle stand, sodass Ihre Antwort sehr wahrscheinlich von denjenigen gelesen wird, die gekommen sind mit der Idee auf.)

C++ 17 (Alle Referenzen stammen aus dem endgültigen Arbeitsentwurf von CPP17 - n4659 )


Einfügung

Sequenzcontainer

  • vector: Die Funktionen insert, emplace_back, emplace, Push_back bewirken eine Neuzuweisung, wenn die neue Größe größer als die alte Kapazität ist. Durch die Neuzuweisung werden alle Verweise, Zeiger und Iteratoren ungültig, die auf die Elemente in der Sequenz verweisen. Wenn keine Neuzuweisung erfolgt, bleiben alle Iteratoren und Verweise vor der Einfügemarke gültig. [26.3.11.5/1]
    In Bezug auf die Funktion reserve macht die Neuzuweisung alle Referenzen, Zeiger und Iteratoren ungültig, die auf die Elemente in der Sequenz verweisen. Während Einfügungen, die nach einem Aufruf von reserve() erfolgen, darf keine Neuzuweisung erfolgen, bis durch eine Einfügung die Größe des Vektors größer als der Wert von capacity() wird. [26.3.11.3/6]

  • deque: Eine Einfügung in die Mitte der Deque macht alle Iteratoren und Verweise auf Elemente der Deque ungültig. Ein Einfügen an einem Ende der Deque macht alle Iteratoren der Deque ungültig, hat jedoch keine Auswirkung auf die Gültigkeit von Verweisen auf Elemente der Deque. [26.3.8.4/1]

  • list: Beeinträchtigt nicht die Gültigkeit von Iteratoren und Referenzen. Wenn eine Ausnahme ausgelöst wird, gibt es keine Auswirkungen. [26.3.10.4/1].
    Die Funktionen insert, emplace_front, emplace_back, emplace, Push_front, Push_back Werden behandelt unter dieser Regel.

  • forward_list: Keine der Überladungen von insert_after Hat Einfluss auf die Gültigkeit von Iteratoren und Referenzen [26.3.9.5/1]

  • array: In der Regel , Iteratoren zu einem Array werden während der gesamten Lebensdauer des Arrays niemals ungültig. Es ist jedoch zu beachten, dass der Iterator während des Austauschs weiterhin auf dasselbe Array-Element verweist und somit seinen Wert ändert.

Assoziative Container

  • All Associative Containers: Die Member insert und emplace haben keinen Einfluss auf die Gültigkeit von Iteratoren und Verweisen auf den Container [26.2.6/9].

Ungeordnete assoziative Container

  • All Unordered Associative Containers: Durch erneutes Waschen werden Iteratoren ungültig gemacht, die Reihenfolge zwischen Elementen geändert und die Elemente in den Buckets geändert, Zeiger oder Verweise auf Elemente werden jedoch nicht ungültig. [26.2.7/9]
    Die Member insert und emplace haben keinen Einfluss auf die Gültigkeit von Verweisen auf Containerelemente, können jedoch alle Iteratoren für den Container ungültig machen. [26.2.7/14]
    Die Member insert und emplace haben keinen Einfluss auf die Gültigkeit von Iteratoren, wenn (N+n) <= z * B, Wobei N die Anzahl der Elemente im Container ist Vor der Einfügeoperation ist n die Anzahl der eingefügten Elemente, B die Anzahl der Container-Container und z der maximale Ladefaktor des Containers. [26.2.7/15]

  • All Unordered Associative Containers: Bei einer Zusammenführungsoperation (z. B. a.merge(a2)) werden Iteratoren, die auf die übertragenen Elemente verweisen, und alle Iteratoren, die auf a verweisen, ungültig, Iteratoren jedoch auf Elemente Verbleibend in a2 bleibt gültig. (Tabelle 91 - Anforderungen an ungeordnete assoziative Container)

Container Adapter

  • stack: vom zugrunde liegenden Container geerbt
  • queue: vom zugrunde liegenden Container geerbt
  • priority_queue: Vom zugrunde liegenden Container geerbt

Löschen

Sequenzcontainer

  • vector: Die Funktionen erase und pop_back machen Iteratoren und Referenzen am oder nach dem Punkt des Löschens ungültig. [26.3.11.5/3]

  • deque: Eine Löschoperation, die das letzte Element eines deque löscht, macht nur den letzten Iterator und alle Iteratoren und Verweise auf die gelöschten Elemente ungültig. Eine Löschoperation, die das erste Element eines deque löscht, aber nicht das letzte Element, macht nur Iteratoren und Verweise auf die gelöschten Elemente ungültig. Eine Löschoperation, die weder das erste noch das letzte Element eines deque löscht, macht den letzten Iterator und alle Iteratoren ungültig und verweist auf alle Elemente des deque. [Hinweis: pop_front Und pop_back Sind Löschvorgänge. —Ende Notiz] [26.3.8.4/4]

  • list: Macht nur die Iteratoren und Verweise auf die gelöschten Elemente ungültig. [26.3.10.4/3]. Dies gilt für die Funktionen erase, pop_front, pop_back, clear.
    Mitgliedsfunktionen remove und remove_if: Löscht alle Elemente in der Liste, auf die durch einen Listeniterator i verwiesen wird, für den die folgenden Bedingungen gelten: *i == value, pred(*i) != false. Macht nur die Iteratoren und Verweise auf die gelöschten Elemente ungültig [26.3.10.5/15].
    unique member function - Löscht alle Elemente außer dem ersten aus jeder aufeinanderfolgenden Gruppe gleicher Elemente, auf die der Iterator i im Bereich [first + 1, last) verweist, für den *i == *(i-1) (für die Version von unique ohne Argumente) oder pred(*i, *(i - 1)) (für die Version von unique mit einem Prädikatargument) gilt. Macht nur die Iteratoren und Verweise auf die gelöschten Elemente ungültig. [26.3.10.5/19]

  • forward_list: erase_after Macht nur Iteratoren und Verweise auf die gelöschten Elemente ungültig. [26.3.9.5/1].
    Mitgliedsfunktionen remove und remove_if - Löscht alle Elemente in der Liste, auf die durch einen Listeniterator i verwiesen wird, für den die folgenden Bedingungen gelten: *i == value (für remove()), pred(*i) ist wahr (für remove_if()). Macht nur die Iteratoren und Verweise auf die gelöschten Elemente ungültig. [26.3.9.6/12].
    unique member function - Löscht alle Elemente außer dem ersten aus jeder aufeinanderfolgenden Gruppe gleicher Elemente, auf die der Iterator i im Bereich [first + 1, last) verweist, für den *i == *(i-1) (für die Version ohne Argumente) oder pred(*i, *(i - 1)) (für die Version mit einem Prädikatargument) gilt. Macht nur die Iteratoren und Verweise auf die gelöschten Elemente ungültig. [26.3.9.6/16]

  • All Sequence Containers: clear macht alle Referenzen, Zeiger und Iteratoren ungültig, die auf die Elemente von a verweisen, und macht möglicherweise den letzten Iterator ungültig (Tabelle 87 - Anforderungen an den Sequenzcontainer). Bei forward_list Macht clear die letzten Iteratoren nicht ungültig. [26.3.9.5/32]

  • All Sequence Containers: assign macht alle Referenzen, Zeiger und Iteratoren ungültig, die auf die Elemente des Containers verweisen. Macht für vector und deque auch den letzten Iterator ungültig. (Tabelle 87 - Anforderungen an Sequenzcontainer)

Assoziative Container

  • All Associative Containers: Die erase -Mitglieder dürfen nur Iteratoren und Verweise auf die gelöschten Elemente ungültig machen. [26.2.6/9]

  • All Associative Containers: Die Member extract machen nur Iteratoren für das entfernte Element ungültig. Zeiger und Verweise auf das entfernte Element bleiben gültig [26.2.6/10]

Container Adapter

  • stack: vom zugrunde liegenden Container geerbt
  • queue: vom zugrunde liegenden Container geerbt
  • priority_queue: Vom zugrunde liegenden Container geerbt

Allgemeine Containeranforderungen für die Ungültigmachung von Iteratoren:

  • Sofern nicht anders angegeben (entweder explizit oder durch Definieren einer Funktion in Bezug auf andere Funktionen), werden durch Aufrufen einer Containerelementfunktion oder Übergeben eines Containers als Argument an eine Bibliotheksfunktion keine Iteratoren für Objekte in diesem Container ungültig gemacht oder die Werte von Objekten in diesem Container geändert . [26.2.1/12]

  • die Funktion no swap() macht Verweise, Zeiger oder Iteratoren ungültig, die auf die Elemente der auszutauschenden Container verweisen. [Hinweis: Der end () -Iterator verweist nicht auf ein Element, daher kann er ungültig werden. —Ende Notiz] [26.2.1/(11.6)]

Als Beispiele für die oben genannten Anforderungen:

  • transform Algorithmus: Die Funktionen op und binary_op dürfen keine Iteratoren oder Unterbereiche ungültig machen oder Elemente in den Bereichen [28.6.4/1] modifizieren.

  • accumulate Algorithmus: Im Bereich [first, last] darf binary_op weder Elemente modifizieren noch Iteratoren oder Teilbereiche ungültig machen [29.8.2/1]

  • reduce Algorithmus: binary_op soll weder Iteratoren oder Unterbereiche ungültig machen, noch Elemente im Bereich [first, last] modifizieren. [29.8.3/5]

und so weiter...

50
P.W

C++ 03 (Quelle: Iterator Invalidation Rules (C++ 03) )


Einfügung

Sequenzcontainer

  • vector: Alle Iteratoren und Referenzen vor dem Einfügepunkt sind nicht betroffen, es sei denn, die neue Containergröße ist größer als die vorherige Kapazität (in diesem Fall sind alle Iteratoren und Referenzen ungültig). [23.2.4.3/1]
  • deque: Alle Iteratoren und Verweise sind ungültig, es sei denn, das eingefügte Element befindet sich an einem Ende (vorne oder hinten) der Deque (in diesem Fall sind alle Iteratoren ungültig, Verweise auf Elemente sind jedoch nicht betroffen) [23.2.1.3/1]
  • list: alle Iteratoren und Referenzen nicht betroffen [23.2.2.3/1]

Assoziative Container

  • [multi]{set,map}: alle Iteratoren und Referenzen unberührt [23.1.2/8]

Containeradapter

  • stack: vom zugrunde liegenden Container geerbt
  • queue: vom zugrunde liegenden Container geerbt
  • priority_queue: vom zugrunde liegenden Container geerbt

Löschen

Sequenzcontainer

  • vector: Jeder Iterator und jede Referenz nach dem Löschpunkt wird ungültig [23.2.4.3/3]
  • deque: Alle Iteratoren und Verweise sind ungültig, es sei denn, die gelöschten Elemente befinden sich am Ende (vorne oder hinten) der Deque (in diesem Fall sind nur Iteratoren und Verweise auf die gelöschten Elemente ungültig). [23.2.1.3/ 4]
  • list: nur die Iteratoren und Verweise auf das gelöschte Element werden ungültig [23.2.2.3/3]

Assoziative Container

  • [multi]{set,map}: nur Iteratoren und Verweise auf die gelöschten Elemente werden ungültig [23.1.2/8]

Containeradapter

  • stack: vom zugrunde liegenden Container geerbt
  • queue: vom zugrunde liegenden Container geerbt
  • priority_queue: vom zugrunde liegenden Container geerbt

Größenänderung

  • vector: gemäß Einfügen/Löschen [23.2.4.2/6]
  • deque: gemäß Einfügen/Löschen [23.2.1.2/1]
  • list: gemäß Einfügen/Löschen [23.2.2.2/1]

Anmerkung 1

Sofern nicht anders angegeben (entweder explizit oder durch Definieren einer Funktion in Bezug auf andere Funktionen), Aufrufen einer Container-Member-Funktion oder Übergeben eines Containers als Argument an Eine Bibliotheksfunktion darf keine Iteratoren für Objekte in diesem Container ungültig machen oder deren Werte ändern. [23.1/11]

Anmerkung 2

In C++ 2003 ist nicht klar, ob "End" -Iteratoren den obigen Regeln unterliegen ; Sie sollten sowieso davon ausgehen, dass dies der Fall ist (wie dies in der Praxis der Fall ist).

Notiz 3

Die Regeln für die Ungültigmachung von Zeigern sind dieselben wie die Regeln für die Ungültigmachung von Referenzen.

C++ 11 (Quelle: Iterator Invalidation Rules (C++ 0x) )


Einfügung

Sequenzcontainer

  • vector: Alle Iteratoren und Referenzen vor dem Einfügepunkt sind nicht betroffen, es sei denn, die neue Containergröße ist größer als die vorherige Kapazität (in diesem Fall sind alle Iteratoren und Referenzen ungültig). [23.3.6.5/1]
  • deque: Alle Iteratoren und Referenzen sind ungültig, es sei denn, das eingefügte Element befindet sich an einem Ende (vorne oder hinten) der Deque (in diesem Fall sind alle Iteratoren ungültig, Verweise auf Elemente sind jedoch nicht betroffen). [23.3.3.4/1]
  • list: Alle Iteratoren und Referenzen unberührt [23.3.5.4/1]
  • forward_list: Alle Iteratoren und Referenzen unberührt (gilt für insert_after) [23.3.4.5/1]
  • array: (n/a)

Assoziative Container

  • [multi]{set,map}: Alle Iteratoren und Referenzen unberührt [23.2.4/9]

Unsortierte assoziative Container

  • unordered_[multi]{set,map}: Alle Iteratoren werden beim erneuten Aufwärmen ungültig, verweisen jedoch nicht auf [23.2.5/8]. Ein erneutes Aufwärmen findet nicht statt, wenn beim Einsetzen die Größe des Containers z * B Nicht überschreitet, wobei z der maximale Ladefaktor und B die aktuelle Anzahl von Eimern ist. [23.2.5/14]

Containeradapter

  • stack: vom zugrunde liegenden Container geerbt
  • queue: vom zugrunde liegenden Container geerbt
  • priority_queue: Vom zugrunde liegenden Container geerbt

Löschen

Sequenzcontainer

  • vector: Jeder Iterator und jede Referenz am oder nach dem Löschpunkt wird ungültig [23.3.6.5/3]
  • deque: Wenn Sie das letzte Element löschen, werden nur Iteratoren und Verweise auf die gelöschten Elemente und den letzten Iterator ungültig. Das Löschen des ersten Elements macht nur Iteratoren und Verweise auf die gelöschten Elemente ungültig. Das Löschen aller anderen Elemente macht alle Iteratoren und Referenzen ungültig (einschließlich des letzten Iterators). [23.3.3.4/4]
  • list: nur die Iteratoren und Verweise auf das gelöschte Element werden ungültig [23.3.5.4/3]
  • forward_list: Nur die Iteratoren und Verweise auf das gelöschte Element werden ungültig (gilt für erase_after) [23.3.4.5/1]
  • array: (n/a)

Assoziative Container

  • [multi]{set,map}: Nur Iteratoren und Verweise auf die gelöschten Elemente werden ungültig [23.2.4/9]

Ungeordnete assoziative Container

  • unordered_[multi]{set,map}: Nur Iteratoren und Verweise auf die gelöschten Elemente werden ungültig [23.2.5/13]

Containeradapter

  • stack: vom zugrunde liegenden Container geerbt
  • queue: vom zugrunde liegenden Container geerbt
  • priority_queue: Vom zugrunde liegenden Container geerbt

Größenänderung

  • vector: gemäß Einfügen/Löschen [23.3.6.5/12]
  • deque: gemäß Einfügen/Löschen [23.3.3.3/3]
  • list: gemäß Einfügen/Löschen [23.3.5.3/1]
  • forward_list: Gemäß Einfügen/Löschen [23.3.4.5/25]
  • array: (n/a)

Anmerkung 1

Sofern nicht anders angegeben (entweder explizit oder durch Definieren einer Funktion in Bezug auf andere Funktionen), Aufrufen einer Container-Member-Funktion oder Übergeben eines Containers als Argument an Eine Bibliotheksfunktion darf keine Iteratoren für Objekte in diesem Container ungültig machen oder deren Werte ändern. [23.2.1/11]

Anmerkung 2

no swap () macht Verweise, Zeiger oder Iteratoren ungültig, die sich auf die Elemente der auszutauschenden Container beziehen. [Anmerkung: Der end () -Iterator verweist nicht auf ein Element, daher kann er ungültig werden . —Ende Notiz] [23.2.1/10]

Notiz 3

Abgesehen von der obigen Einschränkung in Bezug auf swap(), es ist nicht klar, ob "end" -Iteratoren den oben aufgeführten Regeln pro Container unterliegen ; Sie sollten sowieso annehmen, dass sie sind.

Anmerkung 4

vector und alle ungeordneten assoziativen Container unterstützen reserve(n), wodurch garantiert wird, dass keine automatische Größenänderung erfolgt, zumindest bis die Größe des Containers auf n. Vorsicht ist geboten bei ungeordneten assoziativen Containern , da ein zukünftiger Vorschlag die Angabe eines Mindestladefaktors zulässt, der ein erneutes Aufwärmen bei insert nach einer ausreichenden Anzahl von erase Operationen reduzieren die Containergröße unter das Minimum; Die Garantie sollte nach einem erase als potenziell nichtig betrachtet werden.

Es ist wahrscheinlich erwähnenswert, dass ein Insert-Iterator jeglicher Art (std::back_insert_iterator, std::front_insert_iterator, std::insert_iterator) ist garantiert gültig, solange alle Einfügungen über diesen Iterator ausgeführt werden und kein anderes unabhängiges Ereignis eintritt, das den Iterator ungültig macht.

Wenn Sie beispielsweise eine Reihe von Einfügevorgängen in ein std::vector durch die Nutzung std::insert_iterator Es ist durchaus möglich, dass diese Einfügungen eine Neuzuweisung von Vektoren auslösen, wodurch alle Iteratoren ungültig werden, die auf diesen Vektor "zeigen". Es wird jedoch garantiert, dass der betreffende Insert-Iterator gültig bleibt, d. H. Sie können die Sequenz der Insertionen sicher fortsetzen. Es besteht kein Grund zur Sorge, eine Vektorumverteilung auszulösen.

Dies gilt wiederum nur für Einfügungen, die über den Einfügungsiterator selbst ausgeführt werden. Wenn das Ereignis zum Ungültigmachen des Iterators durch eine unabhängige Aktion im Container ausgelöst wird, wird der Iterator zum Einfügen ebenfalls gemäß den allgemeinen Regeln ungültig.

Zum Beispiel dieser 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();

es wird garantiert, dass eine gültige Sequenz von Einfügungen in den Vektor ausgeführt wird, selbst wenn der Vektor "entscheidet", sich irgendwo in der Mitte dieses Prozesses neu zuzuteilen. Der Iterator it wird offensichtlich ungültig, aber it_ins bleibt weiterhin gültig.

40
AnT

Da diese Frage so viele Stimmen hat und zu einer Art FAQ wird, ist es wahrscheinlich besser, eine separate Antwort zu verfassen, um einen signifikanten Unterschied zwischen C++ 03 und C++ 11 in Bezug auf die Auswirkung von std::vector Zu erwähnen. Die Einfügeoperation für die Gültigkeit von Iteratoren und Verweisen in Bezug auf reserve() und capacity(), die die am häufigsten bewertete Antwort nicht bemerkte.

C++ 03:

Durch die Neuzuweisung werden alle Verweise, Zeiger und Iteratoren ungültig, die auf die Elemente in der Sequenz verweisen. Es wird garantiert, dass während Einfügungen, die nach einem Aufruf von reserve () erfolgen, keine Neuzuweisung stattfindet, bis zu dem Zeitpunkt, an dem eine Einfügung die Größe des Vektors ergeben würde größer als die im letzten Aufruf von reserve () angegebene Größe)..

C++ 11:

Durch die Neuzuweisung werden alle Verweise, Zeiger und Iteratoren ungültig, die auf die Elemente in der Sequenz verweisen. Es wird garantiert, dass während Einfügungen, die nach einem Aufruf von reserve () erfolgen, keine Neuzuweisung stattfindet, bis zu dem Zeitpunkt, an dem eine Einfügung die Größe des Vektors ergeben würde größer als der Wert von capacity ().

In C++ 03 ist es also nicht "unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)", wie in der anderen Antwort erwähnt, sondern "greater than the size specified in the most recent call to reserve()". Dies ist eine Sache, die C++ 03 von C++ 11 unterscheidet. In C++ 03 bewirkt eine insert(), dass die Größe des Vektors den im vorherigen reserve() -Aufruf angegebenen Wert erreicht (der durchaus kleiner sein kann als die aktuelle capacity() da eine reserve() eine größere capacity() ergeben könnte als gewünscht, könnte jede nachfolgende insert() eine Neuzuordnung verursachen und alle Iteratoren und Referenzen ungültig machen. In C++ 11 ist dies nicht der Fall, und Sie können immer darauf vertrauen, dass capacity() mit Sicherheit weiß, dass die nächste Neuzuweisung nicht erfolgt, bevor die Größe capacity() überschreitet.

Wenn Sie mit einem C++ 03-Vektor arbeiten und sicherstellen möchten, dass beim Einfügen keine Neuzuweisung erfolgt, ist dies der Wert des Arguments, das Sie zuvor an reserve() übergeben haben sollte die Größe gegen den Rückgabewert eines Aufrufs von capacity() prüfen, sonst könnten Sie sich über eine " verfrühte "Umverteilung.

22
neverhoodboy