Интересно, почему cbegin
и cend
были введены в С++ 11?
Каковы случаи, когда вызов этих методов имеет значение от перегрузки const от begin
и end
?
Интересно, почему cbegin
и cend
были введены в С++ 11?
Каковы случаи, когда вызов этих методов имеет значение от перегрузки const от begin
и end
?
Это довольно просто. Скажем, у меня есть вектор:
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&
к объекту, который он предоставил.
Помимо того, что Никол Болас сказал в своем ответе, рассмотрите новое ключевое слово auto
:
auto iterator = container.begin();
С auto
нет способа убедиться, что begin()
возвращает константный оператор для ссылки на непостоянный контейнер. Итак, теперь вы делаете:
auto const_iterator = container.cbegin();
Возьмите это как практическое использование
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
Назначение не выполняется, потому что it
- неконверсный итератор. Если вы первоначально использовали cbegin, то итератор имел бы правильный тип.
Из 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()
с этого момента любопытно отсутствуют, но они также могут быть добавлены.
Просто наткнулся на этот вопрос... Я знаю это 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
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)
{
// ...
}