What is the reason behind cbegin/cend?

C++C++11IteratorConst CorrectnessConst Iterator

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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 - C++

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 - C++

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.

Solution 5 - C++

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

Solution 6 - C++

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)
{
	// ...
}

Solution 7 - C++

its simple, cbegin returns a constant iterator where begin returns just an iterator

for better understanding lets take two scenarios here

scenario - 1 :

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
	/* code */
	v.push_back(i);
}

for(auto i = v.begin();i< v.end();i++){
	*i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
	cout<<*i<<" ";
}

return 0;
}

this will run because here iterator i is not constant and can be incremented by 5

now let's use cbegin and cend denoting them as constant iterators scenario - 2 :

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
	/* code */
	v.push_back(i);
}

for(auto i = v.cbegin();i< v.cend();i++){
	*i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
	cout<<*i<<" ";
}

return 0;
}

this is not going to work, because you cant update the value using cbegin and cend which returns the constant iterator

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndreyView Question on Stackoverflow
Solution 1 - C++Nicol BolasView Answer on Stackoverflow
Solution 2 - C++Stefan MajewskyView Answer on Stackoverflow
Solution 3 - C++Johannes Schaub - litbView Answer on Stackoverflow
Solution 4 - C++TemplateRexView Answer on Stackoverflow
Solution 5 - C++chris g.View Answer on Stackoverflow
Solution 6 - C++hkBattousaiView Answer on Stackoverflow
Solution 7 - C++user10070450View Answer on Stackoverflow