I wonder why cbegin
and cend
were introduced in C++11?
What are cases when calling these methods makes a difference from const overloads of begin
and end
?
It's quite simple. Say I have a vector:
std::vector<int> vec;
I fill it with some data. Then I want to get some iterators to it. Maybe pass them around. Maybe to std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
In C++03, SomeFunctor
was free to be able to modify the parameter it gets. Sure, SomeFunctor
could take its parameter by value or by const&
, but there's no way to ensure that it does. Not without doing something silly like this:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Now, we introduce cbegin/cend
:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Now, we have syntactic assurances that SomeFunctor
cannot modify the elements of the vector (without a const-cast, of course). We explicitly get const_iterator
s, and therefore SomeFunctor::operator()
will be called with const int &
. If it takes it's parameters as int &
, C++ will issue a compiler error.
C++17 has a more elegant solution to this problem: std::as_const
. Well, at least it's elegant when using range-based for
:
for(auto &item : std::as_const(vec))
This simply returns a const&
to the object it is provided.
Beyond what Nicol Bolas said in his answer, consider the new auto
keyword:
auto iterator = container.begin();
With auto
, there's no way to make sure that begin()
returns a constant operator for a non-constant container reference. So now you do:
auto const_iterator = container.cbegin();
Take this as a practical usecase
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
The assignment fails because it
is a nonconst iterator. If you used cbegin initially, the iterator would have had the right type.
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:
so that a programmer can directly obtain a const_iterator from even a non-const container
They gave this example
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
However, when a container traversal is intended for inspection only, it is a generally preferred practice to use a const_iterator in order to permit the compiler to diagnose const-correctness violations
Note that the working paper also mentions adapter templates, that now have been finalized as std::begin()
and std::end()
and that also work with native arrays. The corresponding std::cbegin()
and std::cend()
are curiously missing as of this time, but they might also be added.
Just stumbled upon this question... I know it's alredy answerd and it's just a side node...
auto const it = container.begin()
is a different type then auto it = container.cbegin()
the difference for int[5]
(using pointer, which i know don't have the begin method but show nicely the difference... but would work in c++14 for std::cbegin()
and std::cend()
, which is essentially what one should use when it's here)...
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
and const_iterator
have inheritance relationship and an implicit conversion occurs when compared with or assigned to the other type.
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)
{
// ...
}
Using cbegin()
and cend()
will increase performance in this case.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
来源:https://stackoverflow.com/questions/12001410/what-is-the-reason-behind-cbegin-cend