What is the reason behind cbegin/cend?

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_iterators, 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.

Leave a Comment