Search code examples
c++c++11iteratorconst-correctnessconst-iterator

What is the reason behind cbegin/cend?


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?


Solution

  • 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.