Zastanawiam się, dlaczego cbegin
i cend
zostały wprowadzone w C ++ 11?
Jakie są przypadki, gdy wywołanie tych metod różni się od stałych przeciążeń begin
i end
?
Zastanawiam się, dlaczego cbegin
i cend
zostały wprowadzone w C ++ 11?
Jakie są przypadki, gdy wywołanie tych metod różni się od stałych przeciążeń begin
i end
?
To całkiem proste. Powiedz, że mam wektor:
std::vector<int> vec;
Wypełniam je pewnymi danymi. Następnie chcę uzyskać iteratory. Może przekażę je. Może std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
W C ++ 03 można SomeFunctor
było swobodnie modyfikować otrzymany parametr. Oczywiście, SomeFunctor
może przyjmować swój parametr według wartości lub według const&
, ale nie ma sposobu, aby się upewnić . Nie bez robienia czegoś tak głupiego:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Teraz przedstawiamy cbegin/cend
:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Teraz mamy gwarancje składniowe, które SomeFunctor
nie mogą modyfikować elementów wektora (oczywiście bez ograniczenia). Dostajemy jawnie const_iterator
s, i dlatego SomeFunctor::operator()
będziemy wywoływani z const int &
. Jeśli zajmie to parametry int &
, C ++ spowoduje błąd kompilatora.
C ++ 17 ma bardziej eleganckie rozwiązanie tego problemu: std::as_const
. Cóż, przynajmniej jest elegancki, gdy używasz zasięgu for
:
for(auto &item : std::as_const(vec))
To po prostu zwraca a const&
do obiektu, który jest udostępniony.
std::cbegin/cend
bezpłatnych funkcji, którestd::begin/std::end
istnieją. Był to nadzór komitetu. Gdyby te funkcje istniały, byłby to na ogół sposób ich użycia.std::cbegin/cend
zostanie dodany w C ++ 14. Zobacz en.cppreference.com/w/cpp/iterator/beginfor(auto &item : std::as_const(vec))
równoważne zfor(const auto &item : vec)
?const
odnośnika. Nicol uważa kontener za const, więcauto
wnioskuje oconst
referencji. IMOauto const& item
jest łatwiejsze i bardziej przejrzyste. Nie jest jasne, dlaczegostd::as_const()
tutaj jest dobrze; Widzę, że przydałoby się coś przekazaćconst
kod inny niż ogólny, w którym nie jesteśmy w stanie kontrolować typu, który jest używany, ale z zasięgiem -for
możemy, więc wydaje mi się, że jest to dodatkowy szum.Poza tym, co powiedział Nicol Bolas w swojej odpowiedzi , rozważ nowe
auto
słowo kluczowe:Dzięki
auto
nie ma sposobu, aby upewnić się, żebegin()
zwraca stały operator dla niestałego odwołania do kontenera. Więc teraz robisz:źródło
const_iterator
jest to tylko kolejny identyfikator. Żadna wersja nie używa wyszukiwania typedefs członkadecltype(container)::iterator
lubdecltype(container)::const_iterator
.const_iterator
zauto
: Napisz szablon funkcji pomocniczej o nazwiemake_const
aby zakwalifikować się do argumentu obiektu.Weź to jako praktyczny przypadek użycia
Przypisanie kończy się niepowodzeniem, ponieważ
it
jest to niekonsekwentny iterator. Jeśli początkowo użyłeś cbegin, iterator miałby odpowiedni typ.źródło
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
Podali ten przykład
Należy zauważyć, że dokument roboczy wspomina również adapter szablonów, które teraz zostały sfinalizowane jak
std::begin()
istd::end()
, a tym także współpracować z rodzimych tablic. Odpowiedniestd::cbegin()
istd::cend()
dziwnie brakuje na ten czas, ale można je również dodać.źródło
Natknąłem się na to pytanie ... Wiem, że to już prawie odpowiedź i to tylko boczny węzeł ...
auto const it = container.begin()
jest wtedy innym typemauto it = container.cbegin()
różnica dla
int[5]
(używając wskaźnika, który, jak wiem, nie ma metody rozpoczęcia, ale ładnie pokazuje różnicę ... ale działałaby w c ++ 14 dlastd::cbegin()
istd::cend()
, co jest zasadniczo tym, czego należy użyć, gdy jest tutaj) ...źródło
iterator
iconst_iterator
mają relację dziedziczenia, a konwersja niejawna następuje w porównaniu z innym typem lub przypisanym do niego.Użycie
cbegin()
icend()
zwiększy wydajność w tym przypadku.źródło
const
główną zaletą jest wydajność (a nie jest to: semantycznie poprawny i bezpieczny kod). Ale, chociaż masz rację, (A)auto
sprawia, że nie jest to problem; (B) Mówiąc o wydajności, przeoczyłeś najważniejszą rzecz, którą powinieneś tutaj zrobić: buforujend
iterator, deklarując jego kopię w stanie inicjującymfor
pętli i porównaj z tym, zamiast otrzymywać nową kopię przez wartość dla każdej iteracji. To poprawi twój punkt widzenia. : Pconst
może zdecydowanie pomóc w osiągnięciu lepszej wydajności, nie z powodu pewnej magii w samymconst
słowie kluczowym, ale dlatego, że kompilator może włączyć pewne optymalizacje, jeśli wie, że dane nie zostaną zmodyfikowane, co inaczej nie byłoby możliwe. Sprawdź ten fragment z przemówienia Jasona Turnera, aby zobaczyć na żywo przykład tego.const
może (prawie pośrednio) przynieść korzyści w zakresie wydajności; na wypadek, gdyby ktoś czytający ten tekst pomyślał: „Nie zawracam sobie głowy dodawaniem,const
jeśli wygenerowany kod nie zostanie w żaden sposób zmieniony”, co nie jest prawdą.jego proste, cbegin zwraca stały iterator, gdzie start zwraca tylko iterator
dla lepszego zrozumienia weźmy tutaj dwa scenariusze
scenariusz - 1:
uruchomi się, ponieważ tutaj iterator i nie jest stały i można go zwiększyć o 5
teraz użyjmy cbegin i cend oznaczamy je jako scenariusz ciągłych iteracji - 2:
to nie zadziała, ponieważ nie można zaktualizować wartości za pomocą cbegin i cend, co zwraca stały iterator
źródło