В чем причина cbegin/cend?

Интересно, почему cbegin и cend были введены в С++ 11?

Каковы случаи, когда вызов этих методов имеет значение от перегрузки const от begin и end?

Ответ 1

Это довольно просто. Скажем, у меня есть вектор:

std::vector<int> vec;

Я заполняю его некоторыми данными. Затем я хочу получить несколько итераторов. Может быть, передать их. Может быть, std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor());

В С++ 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_iterator s, и поэтому SomeFunctor:: operator() будет вызываться с const int &. Если он принимает параметры как int &, С++ выдаст ошибку компилятора.


С++ 17 имеет более элегантное решение этой проблемы: std::as_const. Ну, по крайней мере, это изящно при использовании for на основе диапазона:

for(auto &item : std::as_const(vec))

Это просто возвращает const& к объекту, который он предоставил.

Ответ 2

Помимо того, что Никол Болас сказал в своем ответе, рассмотрите новое ключевое слово auto:

auto iterator = container.begin();

С auto нет способа убедиться, что begin() возвращает константный оператор для ссылки на непостоянный контейнер. Итак, теперь вы делаете:

auto const_iterator = container.cbegin();

Ответ 3

Возьмите это как практическое использование

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

Назначение не выполняется, потому что it - неконверсный итератор. Если вы первоначально использовали cbegin, то итератор имел бы правильный тип.

Ответ 4

Из http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:

так что программист может непосредственно получить const_iterator даже из неконстантный контейнер

Они дали этот пример

vector<MyType> v;

// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
    // use *it ...
}

Однако, когда обход контейнера предназначен только для проверки, обычно рекомендуется использовать const_iterator для того, чтобы разрешить компилятору диагностировать нарушения состязательности.

Обратите внимание, что в рабочем документе также упоминаются шаблоны адаптеров, которые теперь завершены как std::begin() и std::end(), а также работают с собственными массивами. Соответствующие std::cbegin() и std::cend() с этого момента любопытно отсутствуют, но они также могут быть добавлены.

Ответ 5

Просто наткнулся на этот вопрос... Я знаю это alredy answerd, и это просто сторона node...

auto const it = container.begin() - это другой тип, тогда auto it = container.cbegin()

разница для int[5] (используя указатель, который, как я знаю, не имеет метода begin, но хорошо показывает разницу... но будет работать в С++ 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

Ответ 6

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)
{
    // ...
}