Jakie są reguły unieważniania iteratora dla kontenerów C ++?
Najlepiej w formie listy podsumowującej.
(Uwaga: ma to być wpis do często zadawanych pytań na temat C ++ w programie Stack Overflow . Jeśli chcesz skrytykować pomysł podania w tym formularzu odpowiedzi na najczęściej zadawane pytania, to miejsce na meta, które to wszystko rozpoczęło, byłoby odpowiednim miejscem. Odpowiedzi na to pytanie jest monitorowane w czacie C ++ , gdzie pomysł FAQ powstał w pierwszej kolejności, więc twoje odpowiedzi prawdopodobnie zostaną przeczytane przez tych, którzy wpadli na ten pomysł).
Odpowiedzi:
C ++ 17 (Wszystkie odniesienia pochodzą z ostatecznej wersji roboczej CPP17 - n4659 )
Wprowadzenie
Pojemniki z sekwencjami
vector
: Funkcjeinsert
,emplace_back
,emplace
,push_back
przyczyna realokacja jeśli nowy rozmiar jest większy niż stary pojemności. Realokacja unieważnia wszystkie odwołania, wskaźniki i iteratory odnoszące się do elementów w sekwencji. Jeśli nie nastąpi ponowny przydział, wszystkie iteratory i referencje przed punktem wstawienia pozostają ważne. [26.3.11.5/1]W odniesieniu do
reserve
funkcji, realokacja unieważnia wszystkie odwołania, wskaźniki i iteratory odnoszące się do elementów w sekwencji. Przeniesienie nie może nastąpić podczas wstawiania, które następuje po wywołaniu,reserve()
aż do momentu, gdy wstawienie zwiększy rozmiar wektora niż wartośćcapacity()
. [26.3.11.3/6]deque
: Wstawienie w środku deque unieważnia wszystkie iteratory i odniesienia do elementów deque. Wstawienie na każdym końcu deque unieważnia wszystkie iteratory deque, ale nie ma wpływu na ważność odwołań do elementów deque. [26.3.8.4/1]list
: Nie wpływa na ważność iteratorów i referencji. Jeśli zostanie zgłoszony wyjątek, nie ma żadnych efektów. [26.3.10.4/1]. , , , , , Funkcje są objęte tą zasadą.insert
emplace_front
emplace_back
emplace
push_front
push_back
forward_list
: Żadne przeciążenieinsert_after
nie wpłynie na ważność iteratorów i odniesień [26.3.9.5/1]array
: Z reguły iteratory tablicy nigdy nie są unieważniane przez cały okres istnienia tablicy. Należy jednak pamiętać, że podczas wymiany iterator będzie nadal wskazywał ten sam element tablicy, a tym samym zmieni jego wartość.Pojemniki asocjacyjne
All Associative Containers
: Członkowieinsert
iemplace
nie mają wpływu na ważność iteratorów i odniesień do kontenera [26.2.6 / 9]Nieuporządkowane pojemniki asocjacyjne
All Unordered Associative Containers
: Ponowneashowanie unieważnia iteratory, zmiany kolejności elementów i zmiany, w których elementy segmentu pojawiają się, ale nie unieważnia wskaźników ani odwołań do elementów. [26.2.7 / 9]Członkowie
insert
iemplace
nie mają wpływu na ważność odniesień do elementów kontenera, ale mogą unieważnić wszystkie iteratory kontenera. [26.2.7 / 14]Elementy
insert
iemplace
nie mają wpływu na ważność iteratorów, jeżeli(N+n) <= z * B
, gdzie, gdzieN
liczba elementów w pojemniku przed operacją wstawiania,n
jest liczbą wstawionych elementów,B
jest liczbą pojemników pojemnika iz
jest współczynnik maksymalnego obciążenia kontenera. [26.2.7 / 15]All Unordered Associative Containers
: W przypadku operacji scalania (np.a.merge(a2)
), Iteratory odnoszące się do przesłanych elementów i wszystkie iteratory odnoszące sięa
zostaną unieważnione, ale iteratory do pozostałych elementówa2
pozostaną ważne. (Tabela 91 - Nieuporządkowane wymagania dotyczące kontenerów asocjacyjnych)Adaptery pojemników
stack
: odziedziczony z podstawowego konteneraqueue
: odziedziczony z podstawowego kontenerapriority_queue
: odziedziczony z podstawowego konteneraSkasowanie
Pojemniki z sekwencjami
vector
: Funkcjeerase
ipop_back
unieważniają iteratory i odwołania w punkcie kasowania lub po nim. [26.3.11.5/3]deque
: Operacja wymazywania, która usuwa ostatni element elementudeque
nieważnego, unieważnia tylko iterator przeszłości i wszystkie iteratory oraz odniesienia do wymazanych elementów. Operacja wymazywania, która usuwa pierwszy element,deque
ale nie ostatni element, unieważnia tylko iteratory i odwołania do usuniętych elementów. Operacja wymazywania, która nie usuwa ani pierwszego elementu, ani ostatniego elementu a,deque
unieważnia iterator przeszłości i wszystkie iteratory oraz odwołania do wszystkich elementówdeque
. [Uwaga:pop_front
ipop_back
są operacjami usuwania. —Wskazówka] [26.3.8.4/4]list
: Unieważnia tylko iteratory i odwołania do usuniętych elementów. [26.3.10.4/3]. Odnosi się to doerase
,pop_front
,pop_back
,clear
funkcje.remove
iremove_if
funkcje składowe: Usuwa wszystkie elementy z listy, do której odwołuje się iterator listy,i
dla którego spełnione są następujące warunki:*i == value
,pred(*i) != false
. Unieważnia tylko iteratory i odwołania do usuniętych elementów [26.3.10.5/15].unique
funkcja członka - usuwa wszystkie oprócz pierwszego elementu z każdej kolejnej grupy równych elementów, do których odwołuje się iterator,i
w zakresie,[first + 1, last)
dla którego*i == *(i-1)
(dla wersji unikalnej bez argumentów) lubpred(*i, *(i - 1))
(dla wersji unikatowej z argumentem predykatu). Unieważnia tylko iteratory i odwołania do wymazanych elementów. [26.3.10.5/19]forward_list
:erase_after
unieważnia tylko iteratory i odniesienia do usuniętych elementów. [26.3.9.5/1].remove
iremove_if
funkcje składowe - usuwa wszystkie elementy z listy, do których odwołuje się iterator listy i, dla których spełnione są następujące warunki:*i == value
(forremove()
),pred(*i)
true (forremove_if()
). Unieważnia tylko iteratory i odwołania do wymazanych elementów. [26.3.9.6/12].unique
funkcja członka - usuwa wszystkie oprócz pierwszego elementu z każdej kolejnej grupy równych elementów, do których odwołuje się iterator i, w zakresie [pierwszy + 1, ostatni), dla którego*i == *(i-1)
(dla wersji bez argumentów) lubpred(*i, *(i - 1))
(dla wersji z predykatem argument). Unieważnia tylko iteratory i odwołania do wymazanych elementów. [26.3.9.6/16]All Sequence Containers
:clear
unieważnia wszystkie odwołania, wskaźniki i iteratory odnoszące się do elementów a i może unieważnić iterator przeszłości (Tabela 87 - Wymagania dotyczące kontenera sekwencji). Ale dlaforward_list
,clear
nie unieważnia iteratorów przeszłości. [26.3.9.5/32]All Sequence Containers
:assign
unieważnia wszystkie odniesienia, wskaźniki i iteratory odnoszące się do elementów kontenera. Forvector
ideque
, także unieważnia iterator przeszłości. (Tabela 87 - Wymagania dotyczące kontenera sekwencji)Pojemniki asocjacyjne
All Associative Containers
:erase
Członkowie unieważniają tylko iteratory i odniesienia do usuniętych elementów [26.2.6 / 9]All Associative Containers
:extract
Członkowie unieważniają tylko iteratory usuniętego elementu; wskaźniki i odniesienia do usuniętego elementu pozostają ważne [26.2.6 / 10]Adaptery pojemników
stack
: odziedziczony z podstawowego konteneraqueue
: odziedziczony z podstawowego kontenerapriority_queue
: odziedziczony z podstawowego konteneraOgólne wymagania dotyczące kontenera dotyczące unieważnienia iteratora:
O ile nie określono inaczej (jawnie lub przez zdefiniowanie funkcji w kategoriach innych funkcji), wywołanie funkcji elementu kontenera lub przekazanie kontenera jako argumentu do funkcji biblioteki nie unieważnia iteratorów ani nie zmienia wartości obiektów w tym kontenerze . [26.2.1 / 12]
żadna
swap()
funkcja nie unieważnia żadnych odniesień, wskaźników ani iteratorów odnoszących się do elementów wymienianych kontenerów. [Uwaga: Iterator end () nie odnosi się do żadnego elementu, więc może zostać unieważniony. —Wskazówka] [26.2.1 / (11.6)]Jako przykłady powyższych wymagań:
transform
algorytm: Funkcjeop
ibinary_op
nie mogą unieważniać iteratorów lub podzakresów ani modyfikować elementów w zakresach [28.6.4 / 1]accumulate
algorytm: w zakresie [pierwszy, ostatni]binary_op
nie może modyfikować elementów ani unieważniać iteratorów ani podzakresów [29.8.2 / 1]reduce
algorytm: binary_op nie unieważnia iteratorów ani podzakresów, ani nie modyfikuje elementów w zakresie [pierwszy, ostatni]. [29.8.3 / 5]i tak dalej...
źródło
std::string
? Myślę, że różni się odstd::vector
SSOstring
nie spełnia drugiego ogólnego wymagania wymienionego powyżej. Więc go nie uwzględniłem. Próbowałem także trzymać się tego samego wzoru co poprzednie wpisy FAQ."invalidated" can mean "no longer points to what it used to", not just "may not point to any valid element"
że @Marshall Clow opisany w tej odpowiedzi ? Czy wskazuje tylko 1 z 2 warunków?C ++ 03 (Źródło: Iterator Invalidation Rules (C ++ 03) )
Wprowadzenie
Pojemniki z sekwencjami
vector
: wszystkie iteratory i referencje przed punktem wstawienia pozostają niezmienione, chyba że nowy rozmiar kontenera jest większy niż poprzednia pojemność (w którym to przypadku wszystkie iteratory i referencje są unieważnione) [23.2.4.3/1]deque
: wszystkie iteratory i referencje są unieważniane, chyba że wstawiony element znajduje się na końcu (przednim lub tylnym) deque (w którym to przypadku wszystkie iteratory są unieważniane, ale odniesienia do elementów pozostają nienaruszone) [23.2.1.3/1]list
: niezmienione wszystkie iteratory i referencje [23.2.2.3/1]Pojemniki asocjacyjne
[multi]{set,map}
: niezmienione wszystkie iteratory i referencje [23.1.2 / 8]Adaptery pojemników
stack
: odziedziczony z podstawowego konteneraqueue
: odziedziczony z podstawowego kontenerapriority_queue
: odziedziczony z podstawowego konteneraSkasowanie
Pojemniki z sekwencjami
vector
: każdy iterator i odwołanie po punkcie kasowania są unieważnione [23.2.4.3/3]deque
: wszystkie iteratory i odniesienia są unieważniane, chyba że usunięte elementy znajdują się na końcu (z przodu lub z tyłu) deque (w takim przypadku unieważnione są tylko iteratory i odniesienia do usuniętych elementów) [23.2.1.3/4]list
: tylko iteratory i odwołania do usuniętego elementu są unieważniane [23.2.2.3/3]Pojemniki asocjacyjne
[multi]{set,map}
: tylko iteratory i odniesienia do usuniętych elementów są unieważniane [23.1.2 / 8]Adaptery pojemników
stack
: odziedziczony z podstawowego konteneraqueue
: odziedziczony z podstawowego kontenerapriority_queue
: odziedziczony z podstawowego konteneraZmiana rozmiaru
vector
: zgodnie z wstawką / kasowanie [23.2.4.2/6]deque
: jak we wstawieniu / skasowaniu [23.2.1.2/1]list
: zgodnie z wstawieniem / usunięcie [23.2.2.2/1]Notatka 1
Uwaga 2
W C ++ 2003 nie jest jasne, czy iteratory „end” podlegają powyższym regułom ; i tak powinieneś założyć, że są (tak jak ma to miejsce w praktyce).
Uwaga 3
Reguły unieważniania wskaźników są takie same jak reguły unieważniania odwołań.
źródło
C ++ 11 (Źródło: Iterator Invalidation Rules (C ++ 0x) )
Wprowadzenie
Pojemniki z sekwencjami
vector
: wszystkie iteratory i referencje przed punktem wstawienia pozostają niezmienione, chyba że nowy rozmiar kontenera jest większy niż poprzednia pojemność (w którym to przypadku wszystkie iteratory i referencje są unieważnione) [23.3.6.5/1]deque
: wszystkie iteratory i odniesienia są unieważnione, chyba że wstawiony element znajduje się na końcu (przednim lub tylnym) deque (w którym to przypadku wszystkie iteratory są unieważnione, ale odniesienia do elementów pozostają nienaruszone) [23.3.3.4/1]list
: wszystkie iteratory i odwołania nie uległy zmianie [23.3.5.4/1]forward_list
: niezmienione wszystkie iteratory i referencje (dotyczyinsert_after
) [23.3.4.5/1]array
: (nie dotyczy)Pojemniki asocjacyjne
[multi]{set,map}
: niezmienione wszystkie iteratory i referencje [23.2.4 / 9]Niesortowane pojemniki asocjacyjne
unordered_[multi]{set,map}
: wszystkie iteratory zostały unieważnione po ponownym skróceniu, ale odniesienia nie uległy zmianie [23.2.5 / 8]. Ponowne podkasowanie nie nastąpi, jeśli wstawienie nie spowoduje, że rozmiar pojemnika przekroczy wartość,z * B
gdziez
jest maksymalny współczynnik obciążenia iB
bieżąca liczba segmentów. [23.2.5 / 14]Adaptery pojemników
stack
: odziedziczony z podstawowego konteneraqueue
: odziedziczony z podstawowego kontenerapriority_queue
: odziedziczony z podstawowego konteneraSkasowanie
Pojemniki z sekwencjami
vector
: każdy iterator i odwołanie w punkcie kasowania lub po nim jest unieważniony [23.3.6.5/3]deque
: kasowanie ostatniego elementu unieważnia tylko iteratory i odniesienia do skasowanych elementów i iteratora past-the-end; kasowanie pierwszego elementu unieważnia tylko iteratory i odniesienia do skasowanych elementów; usunięcie innych elementów unieważnia wszystkie iteratory i referencje (w tym iterator past-the-end) [23.3.3.4/4]list
: tylko iteratory i odwołania do usuniętego elementu są unieważniane [23.3.5.4/3]forward_list
: tylko iteratory i odniesienia do usuniętego elementu są unieważniane (dotyczyerase_after
) [23.3.4.5/1]array
: (nie dotyczy)Pojemniki asocjacyjne
[multi]{set,map}
: tylko iteratory i odniesienia do usuniętych elementów są unieważniane [23.2.4 / 9]Nieuporządkowane kontenery asocjacyjne
unordered_[multi]{set,map}
: tylko iteratory i odniesienia do usuniętych elementów są unieważniane [23.2.5 / 13]Adaptery pojemników
stack
: odziedziczony z podstawowego konteneraqueue
: odziedziczony z podstawowego kontenerapriority_queue
: odziedziczony z podstawowego konteneraZmiana rozmiaru
vector
: zgodnie z wstawką / kasowanie [23.3.6.5/12]deque
: zgodnie z wstawką / kasowanie [23.3.3.3/3]list
: zgodnie z wstawieniem / usunięcie [23.3.5.3/1]forward_list
: zgodnie z wstawką / kasowanie [23.3.4.5/25]array
: (nie dotyczy)Notatka 1
Uwaga 2
Uwaga 3
Inne niż powyższe zastrzeżenie dotyczące
swap()
, że nie jest jasne, czy „koniec” iteratory podlegają wyżej wymienionych zasad per-kontenera ; i tak powinieneś założyć, że są.Uwaga 4
vector
i obsługuje wszystkie nieuporządkowane pojemniki asocjacyjne,reserve(n)
co gwarantuje, że nie nastąpi automatyczne zmiana rozmiaru przynajmniej do momentu, aż rozmiar kontenera wzrośnien
. Należy zachować ostrożność w przypadku nieuporządkowanych kontenerów asocjacyjnych, ponieważ przyszła propozycja pozwoli na określenie minimalnego współczynnika obciążenia, co pozwoliłoby na ponowne użycieinsert
po wystarczającej liczbieerase
operacji zmniejszających rozmiar kontenera poniżej minimum; gwarancję należy uznać za potencjalnie nieważną poerase
.źródło
swap()
, jakie są zasady ważności iteratora po przypisaniu kopii / przeniesienia?std::basic_string
wydaje się , że nie jest liczony jako kontener, a na pewno nie jako kontener w sekcji standardu, której dotyczy ta uwaga. Gdzie jednak jest napisane, że SSO jest niedozwolone (wiem, że COW jest)?To chyba warto dodać, że iterator insert dowolnego rodzaju (
std::back_insert_iterator
,std::front_insert_iterator
,std::insert_iterator
) gwarantuje, że pozostaną ważne tak długo, jak wszystkie wstawki są wykonywane przez ten iterator i żadna inna niezależna impreza iterator-unieważniania występuje.Na przykład, gdy wykonujesz serię operacji wstawiania do
std::vector
za pomocąstd::insert_iterator
, jest całkiem możliwe, że wstawienia te spowodują realokację wektora, co unieważni wszystkie iteratory, które „wskazują” ten wektor. Jednak iterator wstawiania, o którym mowa, gwarantuje, że pozostanie ważny, tzn. Możesz bezpiecznie kontynuować sekwencję wstawiania. W ogóle nie musisz się martwić o uruchomienie wektorowej alokacji.Dotyczy to również wstawek wykonywanych przez sam iterator wstawek. Jeśli zdarzenie unieważniające iterator zostanie wywołane przez jakieś niezależne działanie na kontenerze, iterator wstawiania również zostanie unieważniony zgodnie z ogólnymi zasadami.
Na przykład ten kod
gwarantuje się, że wykona prawidłową sekwencję wstawek do wektora, nawet jeśli wektor „zdecyduje” o przeniesieniu gdzieś w środku tego procesu. Iterator
it
oczywiście stanie się nieważny, aleit_ins
nadal będzie ważny.źródło
Ponieważ to pytanie przyciąga tyle głosów, a rodzaj pytania staje się często zadawanym pytaniem, lepiej byłoby napisać osobną odpowiedź, aby wspomnieć o jednej znaczącej różnicy między C ++ 03 i C ++ 11 dotyczącej wpływu
std::vector
operacji wstawiania na ważność iteratorów i odniesień doreserve()
icapacity()
, których najbardziej pozytywna odpowiedź nie została zauważona.C ++ 03:
C ++ 11:
Tak więc w C ++ 03 nie jest to „
unless the new container size is greater than the previous capacity (in which case all iterators and references are invalidated)
”, jak wspomniano w innej odpowiedzi, zamiast tego powinno być „greater than the size specified in the most recent call to reserve()
”. Jest to jedna rzecz, którą C ++ 03 różni się od C ++ 11. W C ++ 03, gdy jeden razinsert()
spowoduje, że rozmiar wektora osiągnie wartość określoną w poprzednimreserve()
wywołaniu (która może być mniejsza niż bieżąca,capacity()
ponieważreserve()
wynik może być większycapacity()
niż wymagany), każde następneinsert()
może spowodować realokację i unieważnienie wszystkie iteratory i referencje. W C ++ 11 tak się nie stanie i zawsze możesz miećcapacity()
pewność, że będziesz mieć pewność, że następna realokacja nie nastąpi przed przekroczeniem rozmiarucapacity()
.Podsumowując, jeśli pracujesz z wektorem C ++ 03 i chcesz mieć pewność, że przeniesienie nie nastąpi podczas wykonywania wstawiania, jest to wartość argumentu, który wcześniej przekazałeś
reserve()
, że powinieneś sprawdzić rozmiar, a nie wartość zwrotną połączenia docapacity()
, w przeciwnym razie możesz być zaskoczony „ przedwczesną ” realokacją.źródło
Oto ładna tabela podsumowań z cppreference.com :
W tym przypadku wstawianie odnosi się do dowolnej metody, która dodaje jeden lub więcej elementów do pojemnika, a kasowanie odnosi się do dowolnej metody, która usuwa jeden lub więcej elementów z pojemnika.
źródło