В чем причина cbegin/cend?
интересно, почему cbegin и cend были введены в C++11?
каковы случаи, когда вызов этих методов отличается от перегрузок const begin и end?
6 ответов:
это довольно просто. Скажем, у меня есть вектор:
std::vector<int> vec;я заполняю его некоторыми данными. Тогда я хочу получить некоторые итераторы к нему. Может быть, передать их по кругу. Может быть, чтобы
std::for_each:std::for_each(vec.begin(), vec.end(), SomeFunctor());В C++03,
SomeFunctorбыл свободен, чтобы иметь возможность изменить параметр, который он получает. Конечно,SomeFunctorможет принимать параметр по значению илиconst&, но нет обеспечить что он делает. Не без того, чтобы сделать что-то глупое вроде это:const std::vector<int> &vec_ref = vec; std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());теперь мы вводим
cbegin/cend:std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());теперь у нас есть синтаксические гарантии, что
SomeFunctorневозможно изменить элементы вектора (без const-cast, конечно). Мы явно получаемconst_iterators, а значитSomeFunctor::operator()будет вызван сconst int &. Если он принимает его параметры какint &в C++ выдаст ошибку компилятора.
в C++17 имеет более элегантное решение этой проблемы:
std::as_const. Ну, на по крайней мере, это элегантно при использовании диапазона на основеfor:for(auto &item : std::as_const(vec))это просто возвращает
const&объекту это предоставляется.
помимо того, что сказал Николя Болас в ответ рассмотрим новый
autoключевые слова:auto iterator = container.begin();С
auto, нет никакого способа, чтобы убедиться, чтоbegin()возвращает постоянный оператор для непостоянной ссылки на контейнер. Так что теперь вы делаете:auto const_iterator = container.cbegin();
возьмите это как практический usecase
void SomeClass::f(const vector<int>& a) { auto it = someNonConstMemberVector.begin(); ... it = a.begin(); ... }задание не выполняется, так как
itэто nonconst итератора. Если бы вы изначально использовали cbegin, итератор имел бы правильный тип.
от http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:
так что программист может непосредственно получить const_iterator даже из a неконстантный контейнер
они привели такой пример
vector<MyType> v; // fill v ... typedef vector<MyType>::iterator iter; for( iter it = v.begin(); it != v.end(); ++it ) { // use *it ... }однако, когда обход контейнера предназначен только для проверки, обычно предпочтительно использовать const_iterator для того, чтобы чтобы разрешить компилятору диагностировать с const-корректность нарушения
обратите внимание, что в рабочем документе также упоминаются шаблоны адаптеров, которые теперь были доработаны как
std::begin()иstd::end()и это также работает с собственными массивами. Соответствующееstd::cbegin()иstd::cend()любопытно отсутствуют на данный момент, но они также могут быть добавлены.
просто наткнулся на этот вопрос... Я знаю, что это уже ответ, и это просто боковой узел...
auto const it = container.begin()это другой тип затемauto it = container.cbegin()на
int[5](используя указатель, который, как я знаю, не имеет метода begin, но хорошо показывает разницу... но будет работать в C++14 дляstd::cbegin()иstd::cend(), что, по сути, то, что нужно использовать, когда он здесь)...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
iteratorиconst_iteratorимеют отношение наследования и неявное преобразование происходит при сравнении или присвоении другому типу.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) { // ... }используя
cbegin()иcend()увеличит производительность в этом случае.for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it) { // ... }