What Is the Reason Behind Cbegin/Cend

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.

What is the differences between begin(),end() and cbegin() ,cend()?

cbegin: Returns a const_iterator pointing to the first element in the container.

begin: Returns an iterator pointing to the first element in the sequence.

cend: Returns a const_iterator pointing to the past-the-end element in the container.

end: Returns an iterator pointing to the past-the-end element in the sequence.

http://www.cplusplus.com/reference/map/map/cbegin/
http://www.cplusplus.com/reference/iterator/begin/?kw=begin
http://www.cplusplus.com/reference/map/map/cend/
http://www.cplusplus.com/reference/iterator/end/?kw=end

forcing use of cbegin()/cend() in range-based for

Update: std::as_const will be in C++17, in the <utility> header.

Prior to C++17, there's no built-in syntax for it; however, you can easily write a convenience wrapper:

template<typename T> constexpr const T &as_const(T &t) noexcept { return t; }
for (auto &v: as_const(container))

Note that this calls begin() const rather than cbegin() specifically; the Standard container general requirements specify that cbegin() and begin() const behave identically.

If your container treats non-const iteration specially, it might make sense for it itself to have a member function:

const Container &crange() const noexcept { return *this; }
for (auto &v: container.crange())

cbegin()/cend() vs constBegin()/constEnd()

cbegin() and cend() where introduced for compatibility with Standard Library containers, which all contain such functions since C++11.

Qt just wants to keep it interface similar to the standard library.
constBegin() etc. are just older versions (Qt added them before C++11 was released). There is no difference in using them.

I'd use constBegin() and constEnd() as they are more explicit and 'Qt style', but that just my personal preference. cbegin()/cend() might be used by some algorithms implemented for standard containers (hence their existence in Qt - they help reuse some code). Use them if you expect that at some point you would like to reuse your code outside Qt.

Container begin / end / cbegin / cend semantics, iterator / const_iterator compatibility

iterator begin ();
iterator end ();
const_iterator begin () const;
const_iterator end () const;
const_iterator cbegin () const;
const_iterator cend () const;

And yes, const_iterator it = iterator; should work (but not the other way around), as should == (I am not certain the first is mandated, but you should still do it).

Also consider writing SCARY iterators, where the iterator is not a subtype of the container.

template<class T>
struct foo {
template<class U, std::enable_if_t<std::is_same_v<std::remove_cv_t<U>, std::remove_cv_t<T>>,bool> =true>
friend bool operator==( foo const& lhs, foo<U> const& rhs );
};

This is an example of a == that works between types.

What is the difference between cbegin and begin for vector?

begin will return an iterator or a const_iterator depending on the const-qualification of the object it is called on.

cbegin will return a const_iterator unconditionally.

std::vector<int> vec;
const std::vector<int> const_vec;

vec.begin(); //iterator
vec.cbegin(); //const_iterator

const_vec.begin(); //const_iterator
const_vec.cbegin(); //const_iterator

Why does std::cbegin() not call .cbegin() on the container?

this fails because std::cbegin() calls the .begin()

To be more precise, std::cbegin calls std::begin, which in the generic overload calls c.begin.

For what it's worth, it should be possible to fix gsl::span to return const iterator upon std::cbegin if the designers of gsl specify that there is a specialisation for the generic overload of std::cbegin for gsl::span that uses c.cbegin instead of std::begin, if that is the desired behaviour. I don't know their reasoning for not specifying such specialisation.

As for reasoning for why std::cbegin uses std::begin, I do not know for fact either, but it does have the advantage of being able to support containers that have a c.begin member, but not a c.cbegin member, which can be seen as a less strict requirement, as it can be satisfied by custom containers written prior to C++11, when there was no convention of providing a c.cbegin member function.

Why does std::span lack cbegin and cend methods?

As an update, these are added back by P2278R4. The LWG issue pointed out below is still a problem, x.cbegin() and std::cbegin(x) would do different things, but now x.cbegin() and std::ranges::cbegin(x) do the same thing.


This was removed as a result of LWG3320.

The issue is that x.cbegin() should really do the same thing as std::begin(std::as_const(x)), which is what std::cbegin(x) is defined as.

But that's not the case for std::span, because it doesn't actually own its elements and, as a result, only has shallow const. Given span<int> s;, s.cbegin() would've given you an int const* while std::cbegin(s) gives you an int*. That's very inconsistent. Could've kept s.cbegin() while having it just return begin() (as proposed by PL 247), but that's arguably confusing, so the resolution decision was to simply remove all the const members and aliases.

In any case, std::cbegin(s) always works if you want the container itself to be immutable (which isn't an issue with span to begin with).


Technically, implementation defined, not necessary int const*, but this is a useful fiction for explanatory purposes.

Is cbegin/cend not enough for a range based for loop?

Do I have to implement begin/end?

Yes.

As far as I understood it, it should choose cbegin/cend if it's const auto &x and not auto &x.

That's not how the range-based for is defined in the standard. Range-based for always looks for begin() and end(). From https://timsong-cpp.github.io/cppwp/n3337/stmt.ranged.

otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin and end are looked up with argument-dependent lookup ([basic.lookup.argdep]). For the purposes of this name lookup, namespace std is an associated namespace.



Related Topics



Leave a reply



Submit