What is the reason behind cbegin/cend?
Solution 1:
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.
Solution 2:
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();
Solution 3:
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.
Solution 4:
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.