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.
Solution 5
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
Related videos on Youtube
Comments
-
Andrey almost 2 years
I wonder why
cbegin
andcend
were introduced in C++11?What are cases when calling these methods makes a difference from const overloads of
begin
andend
? -
RobotMan almost 12 yearsI thought the new protocol was cbegin(vec) rather than vec.cbegin().
-
Nicol Bolas almost 12 years@Kaz: There are no
std::cbegin/cend
free functions the way thatstd::begin/std::end
exist. It was an oversight by the committee. If those functions did exist, that would generally be the way to use them. -
allyourcode over 11 yearsCouldn't you do const auto const_iterator = container.begin()?
-
aschepler over 11 years@allyourcode: Doesn't help. To the compiler,
const_iterator
is just another identifier. Neither version uses a lookup of the usual member typedefsdecltype(container)::iterator
ordecltype(container)::const_iterator
. -
allyourcode over 11 years@aschepler I don't understand your second sentence, but I think you missed the "const" in front of "auto" in my question. Whatever auto comes to, seems that const_iterator should be const.
-
aschepler over 11 years@allyourcode: That would give you an iterator which is constant, but that's very different from an iterator to constant data.
-
Adi Shavit over 10 yearsApparently,
std::cbegin/cend
will be added in C++14. See en.cppreference.com/w/cpp/iterator/begin -
Columbo almost 9 yearsThere is a simple way of ensuring that you get a
const_iterator
withauto
: Write an auxiliary function template calledmake_const
to qualify the object argument. -
Stefan Majewsky almost 9 yearsMaybe I'm just not in the C++ mindset anymore, but I can't see a connection between the concepts of "a simple way" and "write an auxiliary function template". ;)
-
Eric over 7 years@Columbo: Why write one, when there's now
std::as_const
. Sostd::as_const(vec).begin()
andvec.cbegin()
are equivalent -
luizfls over 6 years@NicolBolas is
for(auto &item : std::as_const(vec))
equivalent tofor(const auto &item : vec)
? -
underscore_d over 5 years@luizfls Yes. Your code says the item will not be modified by putting the
const
on the reference. Nicol's views the container as const, soauto
deduces aconst
reference. IMOauto const& item
is easier and clearer. It's unclear whystd::as_const()
is good here; I can see it'd be useful when passing something non-const
to generic code where we can't control the type that gets used, but with range-for
, we can, so it just seems like added noise to me there. -
underscore_d over 5 yearsIt took me a while to realise you meant performance is saved by avoiding conversion when initialising & comparing iterators, not the popular myth that
const
's main benefit is performance (which it isn't: it's semantically correct and safe code). But, while you have a point, (A)auto
makes that a non-issue; (B) in talking about performance, you missed a main thing you should've done here: cache theend
iterator by declaring a copy of it in the init-condition of thefor
loop, & compare to that, instead of getting a new copy by value for every iteration. That'll make your point better. :P -
brainplot over 5 years@underscore_d
const
can definitely help achieve better performance, not because of some magic in theconst
keyword itself but because the compiler can enable some optimizations if it knows that the data won't be modified, which would not be possible otherwise. Check this bit from a Jason Turner's talk for a live example of this. -
underscore_d over 5 years@brainplot I didn't say it couldn't. I said that's not its main benefit and that I think it gets overstated, when the real benefit of it is semantically correct and safe code.
-
brainplot over 5 years@underscore_d Yes, I agree on that. I was just making it explicit that
const
can (almost indirectly) lead to performance benefits; just in case somebody reading this may think "I won't bother addingconst
if the generated code isn't affected in any way ever", which is not true. -
Sourabh Choure almost 3 years@allyourcode When you use
const auto const_iterator = container.begin()
it will create a top-level constant and not a low-level constant, as clearly mentioned by @aschepler