为什么"std :: begin()"在这种情况下总是返回"const_iterator"?

xml*_*lmx 17 c++ standards iterator type-safety c++11

#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;
}
Run Code Online (Sandbox Code Playgroud)

我的编译器是clang 4.0.输出是:

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> > >
Run Code Online (Sandbox Code Playgroud)

这意味着:pos_1 = pos_2;没关系,虽然pos_2 = pos_1;不行.

为什么std::begin()总是返回const_iterator而不是iterator在这种情况下?

Pio*_*cki 21

函数调用:

std::declval<std::vector<int>>()
Run Code Online (Sandbox Code Playgroud)

得到一个rvalue表达式,可以表示为:

std::vector<int>&&
Run Code Online (Sandbox Code Playgroud)

编译器有两个(通用)重载std::begin可供选择([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
Run Code Online (Sandbox Code Playgroud)

对于rvalue表达式,只有第二个重载(#2)是可行的 - rvalue不能被非const左值引用绑定.引用类型的const限定意味着编译器将使用begin成员函数的const限定重载:

const_iterator begin() const noexcept;
//                     ~~~~^
Run Code Online (Sandbox Code Playgroud)

返回一个类型的实例const_iterator.

您可以通过std::vector<int>std::declval调用请求左值表达式来更改该行为:

decltype(std::begin(std::declval<std::vector<int>&>())) pos_1 = coll.begin();
//                                             ~~^~~      
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是:如果使用了`begin`的非`静态`成员版本,那么可变版本将是首选,返回`iterator`.这是成员和非成员函数不同的单向方式 (2认同)