Odpowiedzi:
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 SomeFunctorbyło swobodnie modyfikować otrzymany parametr. Oczywiście, SomeFunctormoż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 SomeFunctornie mogą modyfikować elementów wektora (oczywiście bez ograniczenia). Dostajemy jawnie const_iterators, 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/cendbezpłatnych funkcji, które std::begin/std::endistnieją. Był to nadzór komitetu. Gdyby te funkcje istniały, byłby to na ogół sposób ich użycia.
std::cbegin/cendzostanie dodany w C ++ 14. Zobacz en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))równoważne z for(const auto &item : vec)?
constodnośnika. Nicol uważa kontener za const, więc autownioskuje o constreferencji. IMO auto const& itemjest łatwiejsze i bardziej przejrzyste. Nie jest jasne, dlaczego std::as_const()tutaj jest dobrze; Widzę, że przydałoby się coś przekazać constkod inny niż ogólny, w którym nie jesteśmy w stanie kontrolować typu, który jest używany, ale z zasięgiem - formożemy, więc wydaje mi się, że jest to dodatkowy szum.
Poza tym, co powiedział Nicol Bolas w swojej odpowiedzi , rozważ nowe autosłowo kluczowe:
auto iterator = container.begin();
Dzięki autonie ma sposobu, aby upewnić się, że begin()zwraca stały operator dla niestałego odwołania do kontenera. Więc teraz robisz:
auto const_iterator = container.cbegin();
const_iteratorjest to tylko kolejny identyfikator. Żadna wersja nie używa wyszukiwania typedefs członka decltype(container)::iteratorlub decltype(container)::const_iterator.
const_iteratorz auto: Napisz szablon funkcji pomocniczej o nazwie make_constaby zakwalifikować się do argumentu obiektu.
Weź to jako praktyczny przypadek użycia
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
Przypisanie kończy się niepowodzeniem, ponieważ itjest to niekonsekwentny iterator. Jeśli początkowo użyłeś cbegin, iterator miałby odpowiedni typ.
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
dzięki czemu programiści mogą bezpośrednio uzyskać const_iterator z nawet kontenera non-const
Podali ten przykład
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
Jednak gdy przejście kontenera jest przeznaczone wyłącznie do inspekcji, ogólnie preferowaną praktyką jest użycie const_iterator w celu umożliwienia kompilatorowi diagnozowania naruszeń const
Należy zauważyć, że dokument roboczy wspomina również adapter szablonów, które teraz zostały sfinalizowane jak std::begin()i std::end(), a tym także współpracować z rodzimych tablic. Odpowiednie std::cbegin()i std::cend()dziwnie brakuje na ten czas, ale można je również dodać.
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 typem auto 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 dla std::cbegin()i std::cend(), co jest zasadniczo tym, czego należy użyć, gdy jest tutaj) ...
int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers); // type is int const* -> value is const
iteratori const_iteratormają relację dziedziczenia, a konwersja niejawna następuje w porównaniu z innym typem lub przypisanym do niego.
class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
// ...
}
Użycie cbegin()i cend()zwiększy wydajność w tym przypadku.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
constgłówną zaletą jest wydajność (a nie jest to: semantycznie poprawny i bezpieczny kod). Ale, chociaż masz rację, (A) autosprawia, że nie jest to problem; (B) Mówiąc o wydajności, przeoczyłeś najważniejszą rzecz, którą powinieneś tutaj zrobić: buforuj enditerator, deklarując jego kopię w stanie inicjującym forpętli i porównaj z tym, zamiast otrzymywać nową kopię przez wartość dla każdej iteracji. To poprawi twój punkt widzenia. : P
constmoże zdecydowanie pomóc w osiągnięciu lepszej wydajności, nie z powodu pewnej magii w samym constsł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.
constmoż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, constjeś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:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
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:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
to nie zadziała, ponieważ nie można zaktualizować wartości za pomocą cbegin i cend, co zwraca stały iterator