Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "std::begin()" always return "const_iterator" in such a case?

#include <vector>
#include <iostream>

using namespace std;

int main()
{
    vector<int> coll;

    decltype(std::begin(std::declval<vector<int>>()))
        pos_1 = coll.begin();
    auto pos_2 = coll.begin();

    cout << typeid(decltype(pos_1)).name() << endl;
    cout << typeid(decltype(pos_2)).name() << endl;
}

My compiler is clang 4.0. The output is:

class std::_Vector_const_iterator<class std::_Vector_val<struct std::_Simple_types<int> > >
class std::_Vector_iterator<class std::_Vector_val<struct std::_Simple_types<int> > >

That means: pos_1 = pos_2; is ok, while pos_2 = pos_1; is not ok.

Why does std::begin() always return const_iterator rather than iterator in such a case?

like image 795
xmllmx Avatar asked Mar 03 '17 13:03

xmllmx


2 Answers

The function call:

std::declval<std::vector<int>>()

results in an rvalue expression that can be denoted as:

std::vector<int>&&

The compiler has two (generic) overloads of std::begin to choose from ([iterator.range]):

template <class C> 
auto begin(C& c) -> decltype(c.begin());        // #1

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

For an rvalue expression, only the second overload (#2) is viable -- an rvalue cannot be bound by a non-const lvalue reference. The const qualification of the referenced type implies that the compiler will use the const qualified overload of the begin member function:

const_iterator begin() const noexcept;
//                     ~~~~^

which returns an instance of type const_iterator.

You can change that behavior by requesting an lvalue expression of std::vector<int> from the std::declval call:

decltype(std::begin(std::declval<std::vector<int>&>())) pos_1 = coll.begin();
//                                             ~~^~~      
like image 114
Piotr Skotnicki Avatar answered Oct 19 '22 21:10

Piotr Skotnicki


if you have Type&& (a temporary) then overload-resolution will prefer const Type& over Type&, since a temporary wont bind to a non-const lvalue-reference

like image 3
sp2danny Avatar answered Oct 19 '22 19:10

sp2danny