Reputation: 181
For std::begin
, we have two overloads for containers:
template< class C >
auto begin( C& c ) -> decltype(c.begin());
template< class C >
auto begin( const C& c ) -> decltype(c.begin());
But the constness of C
can be deduced by the usual template deduction rules, so it seems like the second overload is redundant. What am I missing?
Upvotes: 16
Views: 728
Reputation: 60999
It's reasonable to call begin
(and end
, for that matter) on an rvalue, provided we don't use the resulting iterator after the container has been destroyed. However, passing an rvalue to a parameter of the form T&
will not work, which is where the second overload comes into play.
However, it may well be that we're dealing with a thoughtless transformation of a former range-based for proposal wording:
Add the following to the end of [container.concepts.member]:
template<Container C> concept_map Range<C> { typedef C::iterator iterator; iterator begin( C& c ) { return Container<C>::begin(c); } iterator end( C& c ) { return Container<C>::end(c); } }; template<Container C> concept_map Range<const C> { typedef C::const_iterator iterator; iterator begin( const C& c ) { return Container<C>::begin(c); } iterator end( const C& c ) { return Container<C>::end(c); } };
When it became clear that concepts weren't going to make it into C++11, papers were amended, and all four function temploids were presumably translated into equivalent namespace-scope function templates. This had the (potentially unintended) consequence of rvalues being accepted, while the original code was simply intended to distinguish between differently qualified container types.
Note that a modern implementation of begin
/end
would use forwarding references instead - e.g.
template <typename T>
constexpr auto begin(T&& t)
-> decltype(std::forward<T>(t).begin()) {
return std::forward<T>(t).begin();
}
Upvotes: 10