为什么编译器需要尾随返回类型?

Jiv*_*son 5 c++ for-loop visual-c++ c++11

我正在使用适配器来使用基于范围的for循环来反向迭代.(我不知道用于此目的的增强适配器("适配器").如果它是我已下载的自由轮,我非常相信不重新发明轮子.)

让我感到困惑的是,除非我在下面的代码中使用尾随返回类型,否则VC++ 2012会不高兴为什么:

#include <string>
#include <iostream>

template<class Fwd>
struct Reverser {
    const Fwd &fwd;
    Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
    auto begin() -> decltype(fwd.rbegin()) const { return fwd.rbegin(); } 
    auto end() ->   decltype(fwd.rend())   const { return fwd.rend(); } 
};

template<class Fwd>
Reverser<Fwd> reverse(const Fwd &fwd) { return Reverser<Fwd>(fwd); }

int main() {
    using namespace std;
    const string str = ".dlrow olleH";
    for(char c: reverse(str)) cout << c;
    cout << endl;
}
Run Code Online (Sandbox Code Playgroud)

当我尝试以下操作时,我得到了错误,"错误C2100:非法间接","错误C2228:'.'sbegin'的左边必须有class/struct/union".我错过了什么?

template<class Fwd>
struct Reverser {
    const Fwd &fwd;
    Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
    decltype(fwd.rbegin()) begin() const { return fwd.rbegin(); } 
    decltype(fwd.rend())   end() const { return fwd.rend(); } 
};
Run Code Online (Sandbox Code Playgroud)

更新:根据关于"this"指针的讨论,我尝试了另一种方法.看马云,不,这个!它编译得很好.我确实相信,无论是对还是错,VC++都没有意识到这一点.

template<class Fwd>
struct Reverser {
    const Fwd &fwd;
    Reverser<Fwd>(const Fwd &fwd_): fwd(fwd_) {}
    decltype(((const Fwd*)0)->rbegin()) begin() const { return fwd.rbegin(); } 
    decltype(((const Fwd*)0)->rend())   end()   const { return fwd.rend(); } 

};
Run Code Online (Sandbox Code Playgroud)

更新2:提交给MS:https://connect.microsoft.com/VisualStudio/feedback/details/765455/vc-2012-compiler-refuses-decltype-return-spec-for-member-function

更新3:从VC++ 2015开始修复此问题.谢谢,微软的人.

Die*_*ühl 5

从OP注意:VC++是错误的.VC++ 2015正确接受代码.

我原来的回答是VC++拒绝的代码在标准中被禁止实际上是错误的:正如Johannes Schaub所指出的那样:在5.1 [expr.prim.general]第12段中,描述了一个id表达式表示非可以使用静态数据成员或非静态成员函数.特别是,最后一个子弹说明:

如果该id-expression表示非静态数据成员,则它出现在未评估的操作数中.

表达式decltype(expr)是一个未评估的操作数.此外,9.3.1 [class.mfct.non-static]第3段解释了this隐式添加到表达式的情况:

当一个id-expression(5.1)不属于类成员访问语法(5.2.5)并且不用于形成指向成员(5.3.1)的指针时,在上下文中的类X的成员中使用可以使用(5.1.1),如果名称查找(3.4)将id-expression中的名称解析为某个类C的非静态非类型成员,并且如果id-expression可能被评估或者C是X或X的基类,id-expression被转换为类成员访问表达式(5.2.5),使用(*this)(9.3.2)作为左侧的后缀表达式.运营商.

所讨论的上下文不是"潜在评估",并且没有涉及的基础.因此,this不添加,也不必在范围内.总之,这意味着声明

decltype(fwd.rbegin()) begin() const;
Run Code Online (Sandbox Code Playgroud)

应该是合法的.好像在用

decltype(static_cast<Reverser<Fwd> const*>(0)->fwd.rbegin()) begin() const;
Run Code Online (Sandbox Code Playgroud)

是在未正确实现编译器的情况下解决的问题.