The C ++ 11 standard states that std :: begin (Container &&) returns const_iterator?

Here is a link to the corresponding code:

#include <iostream> #include <string> #include <vector> #include <type_traits> int main() { std::vector<int> v{1, 2, 3, 4, 5}; auto iter = begin(std::move(v)); if(std::is_const<typename std::remove_reference<decltype(*iter)>::type>::value) std::cout<<"is const\n"; return 0; } 

http://coliru.stacked-crooked.com/a/253c6373befe8e50

I came across this behavior due to declval<Container>() in the decltype with std::begin . Both gcc and clang return iterators that return constant references when dereferencing. This probably makes sense, since references to r-values ​​are usually associated with obsolete objects that you do not want to modify. However, I could not find any documentation on this issue to determine if it is mandatory in accordance with the standard. I could not find any matching begin() overloads or Container::begin() overridden overloads.

Update: The answers clarified what is happening, but the interactions can be subtle, as shown below:

 #include <iostream> #include <string> #include <vector> #include <type_traits> int main() { if(std::is_const<typename std::remove_reference<decltype(*begin(std::declval<std::vector<std::string>>()))>::type>::value) std::cout<<"(a) is const\n"; if(!std::is_const<typename std::remove_reference<decltype(*std::declval<std::vector<std::string>>().begin())>::type>::value) std::cout<<"(b) is not const\n"; if(!std::is_const<typename std::remove_reference<decltype(*begin(std::declval<std::vector<std::string>&>()))>::type>::value) std::cout<<"(c) is not const\n"; return 0; } 

http://coliru.stacked-crooked.com/a/15c17b288f8d69bd

Naively, you do not expect different results for (a) and (b) when :: begin is defined only in terms of calling vector :: begin. However, the lack of overloads of std :: begin, which take a non-constant reference to the r-value and return an iterator (or overloads with the qualification vector :: begin, which return const_iterator), causes exactly this.

+6
source share
2 answers

Try to analyze what happens step by step:

  • You call std::begin(std::vector<int>&&) , but std::begin does not have an overload that takes the value r :

     template< class C > auto begin( C& c ) -> decltype(c.begin()); template< class C > auto begin( const C& c ) -> decltype(c.begin()); 

  1. Because of the write - off of links, the temporary (xvalue) is bound only to the const lvalue reference:

    If you call Fwd with xvalue, we again get Type && as type v. This will not allow you to call a function that accepts a non-const lvalue value, since the x value cannot be bound to the lvalue constant reference. It can bind to a const lvalue reference, so if Call used const and am, we could call Fwd with xvalue.

    (From a related answer).


    1. Hence,

        template<class C> auto begin(const C& c) -> decltype(c.begin()); 

      an overload is called that returns a const iterator.

      Why?

      Because std::begin(v) calls v.begin() , which returns const_iterator when const instances of std::vector called.

+5
source

As you can see at http://en.cppreference.com/w/cpp/iterator/begin , interesting overloads:

 template<class C> auto begin(C& c) -> decltype(c.begin()); template<class C> auto begin(const C& c) -> decltype(c.begin()); 

and std::vector<int>&& can only communicate with the second overload (therefore returns const_iterator ).

+7
source

All Articles