Jaka jest różnica między const_iterator i non-const iterator w C ++ STL?

139

Jaka jest różnica między a const_iteratori an iteratori gdzie użyłbyś jednego nad drugim?

Konrad
źródło
1
Cóż, nazwa const_iterator brzmi tak, jakby iterator to const, podczas gdy to, na co wskazuje ten iterator, to faktyczna stała.
talekeDskobeDa

Odpowiedzi:

124

const_iterators nie pozwalają na zmianę wartości, na które wskazują, zwykłe iterators to robią.

Podobnie jak w przypadku wszystkich rzeczy w C ++, zawsze preferuj const, chyba że istnieje dobry powód, aby używać zwykłych iteratorów (tj. Chcesz wykorzystać fakt, że nie constzmieniają one wskazanej wartości).

Dominic Rodger
źródło
8
W idealnym świecie tak by się stało. Ale z C ++ const jest tak dobre, jak osoba, która napisała kod :(
JaredPar
mutable istnieje z bardzo dobrego powodu. Jest rzadko używany, a patrząc na wyszukiwarkę Google Code Search, wydaje się, że istnieje spory procent prawidłowych zastosowań. Słowo kluczowe jest bardzo potężnym narzędziem optymalizacyjnym i nie jest tak, że jego usunięcie poprawiłoby poprawność stałej ( kaszel i kaszel )
coppro
2
Bardziej jak potężny hack. Ze wszystkich przypadków użycia słowa kluczowego „mutable”, jakie kiedykolwiek widziałem, wszystkie z wyjątkiem jednego były dokładnym wskaźnikiem, że kod był źle napisany i że był potrzebny jako hack, aby obejść defekty.
John Dibling
7
Ma uzasadnione zastosowania, takie jak buforowanie wyników długich obliczeń w klasie stałej. Z drugiej strony, to właściwie jedyny raz, kiedy używałem mutable w ciągu prawie dwudziestu lat programowania w C ++.
Head Geek
Ogólnym przykładem użycia const_iterator byłoby, gdy iterator jest wartością r
talekeDskobeDa
40

Powinny być oczywiste. Jeśli iterator wskazuje na element typu T, to const_iterator wskazuje na element typu „const T”.

Zasadniczo jest to odpowiednik typów wskaźników:

T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator

Iterator const zawsze wskazuje na ten sam element, więc sam iterator to const. Ale element, na który wskazuje, nie musi być const, więc element, na który wskazuje, można zmienić. Const_iterator to iterator wskazujący na element const, więc chociaż sam iterator może być aktualizowany (na przykład zwiększany lub zmniejszany), element, na który wskazuje, nie może zostać zmieniony.

jalf
źródło
1
„Iterator const zawsze wskazuje na ten sam element”. To jest niepoprawne.
John Dibling
2
Jak to? Zwróć uwagę na brakujące podkreślenie. Kontrastuję zmienną typu const std :: vector <T> :: iterator ze std :: vector <T> :: const_iterator. W pierwszym przypadku sam iterator jest stały, więc nie można go modyfikować, ale element, do którego się odwołuje, można dowolnie modyfikować.
jalf
4
O, rozumiem. Tak, brakowało mi podkreślenia.
John Dibling
3
@JohnDibling Upvoted za wyjaśnienie subtelności między const iteratera const_iterator.
legends2k
8

Niestety, wiele metod dla kontenerów STL przyjmuje jako parametry iteratory zamiast const_iterators . Więc jeśli masz const_iterator , nie możesz powiedzieć „wstaw element przed elementem, na który wskazuje ten iterator” (mówienie takiej rzeczy nie jest koncepcyjnie naruszeniem const, moim zdaniem). Jeśli mimo wszystko chcesz to zrobić, musisz przekonwertować go na iterator inny niż const za pomocą std :: advanced () lub boost :: next () . Na przykład. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Jeśli kontener jest std :: list , to czas wykonywania tego wywołania będzie wynosił O (n) .

Tak więc uniwersalna zasada dodawania const wszędzie tam, gdzie jest to „logiczne”, jest mniej uniwersalna, jeśli chodzi o kontenery STL.

Jednak kontenery boost przyjmują const_iterators (np. Boost :: unordered_map :: erase ()). Więc kiedy używasz pojemników przyspieszających, możesz być „agresywny”. Swoją drogą, czy ktoś wie, czy lub kiedy kontenery STL zostaną naprawione?

Magnus Andermo
źródło
1
To może być kwestia opinii. W przypadku vectori dequewstawienie jednego elementu unieważnia wszystkie istniejące iteratory, co nie jest bardzo const. Ale rozumiem twój punkt widzenia. Takie operacje są chronione przez kontenerowość const, a nie iteratory. Zastanawiam się, dlaczego w standardowym interfejsie kontenera nie ma funkcji konwersji iteratora ze stałej na niekonstentną.
Potatoswatter
Masz rację Potatoswatter, jestem kategoryczny, to kwestia opinii dla kontenerów o swobodnym dostępie i container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) i tak jest O (1) . Zastanawiam się też, dlaczego nie ma funkcji konwersji dla kontenerów o nieregularnym dostępie, ale może jest dobry powód? Czy wiesz, czy jest jakiś powód, dla którego funkcje dla kontenerów o dostępie innym niż swobodny nie przyjmują const_iterators ?
Magnus Andermo,
„Mówienie czegoś takiego nie jest koncepcyjnie naruszeniem const, moim zdaniem” - to bardzo ciekawy komentarz, mam następujące przemyślenia na ten temat. Z prostych wskazówek, można powiedzieć, int const * foo; int * const foo;i int const * const foo;wszystkie trzy są ważne i użyteczne, każdy na swój sposób. std::vector<int> const barpowinien być taki sam jak drugi, ale niestety często jest traktowany jak trzeci. Podstawową przyczyną problemu jest to, że nie możemy powiedzieć, std::vector<int const> bar;kiedy oznacza, że ​​nie ma sposobu, aby uzyskać taki sam efekt, jak int const *foo;w wektorze.
dgnuff
5

Używaj const_iterator, kiedy tylko możesz, używaj iteratora, gdy nie masz innego wyboru.

Naveen
źródło
4

Minimalne przykłady do uruchomienia

Iteratory inne niż const pozwalają modyfikować to, na co wskazują:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

Iteratory stałych nie:

const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

Jak pokazano powyżej, v.begin()jest constprzeciążony i zwraca albo iteratorlub w const_iteratorzależności od stałej zmiennej kontenera:

Typowym przypadkiem const_iteratorwyskakującego okienka thisjest użycie wewnątrz constmetody:

class C {
    public:
        std::vector<int> v;
        void f() const {
            std::vector<int>::const_iterator it = this->v.begin();
        }
        void g(std::vector<int>::const_iterator& it) {}
};

consttworzy thisconst, co tworzy this->vconst.

Zwykle możesz o tym zapomnieć za pomocą auto, ale jeśli zaczniesz przekazywać te iteratory, będziesz musiał pomyśleć o nich przy sygnaturach metod.

Podobnie jak const i non-const, możesz łatwo konwertować z non-const na const, ale nie na odwrót:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();

// non-const to const.
std::vector<int>::const_iterator cit = it;

// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

// Compile time error: no conversion from const to no-const.
//it = ci1;

Którego użyć: analogicznie do const intvs int: preferuj iteratory const, kiedy tylko możesz ich użyć (kiedy nie musisz modyfikować za ich pomocą kontenera), aby lepiej udokumentować zamiar czytania bez modyfikowania.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
0

(jak powiedzieli inni) const_iterator nie pozwala na modyfikowanie elementów, na które wskazuje, jest to przydatne w metodach klasy const. Pozwala także wyrazić swoje zamiary.

Trey Jackson
źródło
0

ok Pozwólcie, że wyjaśnię to najpierw na bardzo prostym przykładzie bez użycia stałego iteratora weź pod uwagę, że mamy zbiór losowych liczb całkowitych zbiór "randomData"

    for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;

Jak widać do zapisu / edycji danych wewnątrz kolekcji używany jest normalny iterator, ale do odczytu użyto stałego iteratora. Jeśli spróbujesz użyć stałego iteratora w pierwszej pętli for, otrzymasz błąd. Zasadą jest używanie stałego iteratora do odczytu danych w kolekcji.

Panie Coder
źródło