尾随返回类型的名称查找和类型简化规则是什么?

Vit*_*meo 6 c++ return-type language-lawyer c++11 trailing-return-type

尾随返回类型允许在以下两种情况下简化代码:

  1. 从类的成员函数之一返回在类内部定义的类型:

    struct X
    {
        using foo = int;
        foo f();
    };
    
    // pre-C++11
    X::foo X::f()      { /* ... */ }
    
    // trailing, doesn't require `X::` before `foo`
    auto X::f() -> foo { /* ... */ }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 返回复杂的类型,例如函数指针类型:

    // pre-C++11
    int(*g(float))(int) { /* ... */ }
    
    // trailing, easier to read
    auto f(float) -> int(*)(int) { /* ... */ }
    
    Run Code Online (Sandbox Code Playgroud)

我正在尝试查找Standard的相关部分,以解释上述两种简化的工作方式。我已经看过[basic.lookup]并略过了trailing-return,但是找不到任何简单的方法可以解释上述转换的工作原理。

我错过了吗?

标准的哪些部分解释了以上的尾随回返型简化形式?

Bri*_*ian 2

对于 #1,请参阅 C++17 [basic.lookup.qual]/3:

\n\n
\n

在declarator-idQualified-id的声明中,在声明的Qualified-id之前使用的名称会在定义的命名空间范围中查找;限定 ID后面的名称在成员的类或命名空间的范围内查找

\n
\n\n

普通的前导返回类型位于declarator-id之前,即X::f在命名空间范围内查找它。它后面有一个尾随返回类型,因此可以在类作用域中查找它。

\n\n

对于 #2,请观察Trailing-return-type的语法对于 #2,观察[dcl.decl]/4 中的

\n\n
\n

-> 类型 ID

\n
\n\n

根据 [dcl.fct]/2,该类型是函数的返回类型。

\n\n

如果要使用前导返回类型,则必须通过 [dcl.fct]/1 递归确定函数的返回类型:

\n\n
\n

在声明中,T D其中D以下形式

\n\n
\n

D1 ( 参数声明子句 ) cv-qualifier-seq (opt) ref-qualifier (opt) noexcept-specifier (opt) attribute-specifier-seq (opt)

\n
\n\n

声明中包含的declarator-id的类型T D1为 \xe2\x80\x9c派生声明符类型列表 \xe2\x80\x9d,其中declarator-idT的类型为 \xe2\x80\x9c派生-声明符类型列表noexcept (opt) 参数声明子句的函数\n cv-qualifier-seq (opt) ref-qualifier (opt) 返回D ()T\xe2\x80\x9d,其中 ...

\n
\n\n

这里,T代表一个decl-specifier-seq。如果你有一个typedef-name表示int(*)(int),比如说,FPII,那么你可以使用它:

\n\n
FPII g(float);\n
Run Code Online (Sandbox Code Playgroud)\n\n

但如果你想以困难的方式做到这一点,我们必须找到TD1,这样当导出声明符类型列表类型转换的序列将根据 的语法形式D1施加)应用于“函数”时,返回“,结果是”返回指针的函数(返回的函数TD1intTfloatintint ”。

\n\n

float如果派生声明符类型列表是“返回指针的函数”并且T是,则满足这一点int。因此,声明符D1必须具有语法形式* declarator-id (float),以便产生所述派生声明符类型列表。我们必须添加一对额外的括号,以便在整个声明中获得正确的绑定。

\n\n

这里没有发生从尾随返回类型到前导返回类型的“转换”。相反,尾随返回类型仅允许您直接指定返回类型,而前导返回类型则由递归解包声明符的算法进行解释。虽然这在“声明遵循使用”的原则下是有意义的,但对于人类(包括经验丰富的 C++ 程序员)来说,直观地掌握它往往有点困难。尤其是当我们必须反向执行时(写下声明,而不是解释现有声明)。

\n