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

What are the name lookup and type simplification rules for trailing return types?

尾随 return 类型允许在这两种情况下简化代码:

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

    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 { /* ... */ }
    
  2. 返回一个复杂的类型,比如函数指针类型:

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

我试图找到标准的相关部分来解释上述两个简化的工作原理。我查看了 [basic.lookup] 并搜索了 trailing-return,但找不到任何直接解释上述转换如何工作的内容。

我错过了吗?

标准的哪些部分解释了上述 trailing-return-type 简化?

IMO,你有两个不相关的问题,我会尽量回答第一个。
它被 [basic.scope.class]/1.5:

覆盖

The potential scope of a declaration that extends to or past the end of a class definition also extends to the regions defined by its member definitions, even if the members are defined lexically outside the class (this includes static data member definitions, nested class definitions, member function definitions (including the member function body and any portion of the declarator part of such definitions which follows the declarator-id, including a parameter-declaration-clause and any default arguments ([dcl.fct.default]).

在out-of-class定义成员函数

auto X::f() -> foo { /* ... */ }

trailing-return-typedeclarator-id X::f 之后,所以它是class 成员的潜在范围,因此当 foo 被提及时,非限定查找会找到 X::foo

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

In a declaration in which the declarator-id is a qualified-id, names used before the qualified-id being declared are looked up in the defining namespace scope; names following the qualified-id are looked up in the scope of the member's class or namespace.

一个普通的前导 return 类型在 declarator-id 之前,即 X::f 所以它在命名空间范围内查找。它后面跟着一个尾随 return 类型,所以它在 class 范围内查找。

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

-> type-id

并且根据 [dcl.fct]/2,该类型是函数的 return 类型。

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

In a declaration T D where D has the form

D1 ( parameter-declaration-clause ) cv-qualifier-seq(opt) ref-qualifier(opt) noexcept-specifier(opt) attribute-specifier-seq(opt)

声明T D1中包含的declarator-id的类型是“derived-declarator-type-list T”,declarator-idD中的类型是“derived-declarator-type-list noexcept(opt) (参数声明子句)的函数 cv-qualifier-seq(opt) ref-qualifier(opt) returning T”,其中 .. .

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

FPII g(float);

但是如果你想用困难的方式做到这一点,我们必须找到 TD1 这样当 derived-declarator-type-list 类型转换序列 D1 将根据 D1 的句法形式施加于 T,应用于 "function of int returning T",结果为 "function of float returning pointer to (function of int returning int)" .

如果 derived-declarator-type-list 是 "function of float returning pointer to",并且 Tint,这将得到满足。因此,声明符 D1 必须具有语法形式 * declarator-id (float) 才能生成所述派生声明符类型列表。我们必须添加一对额外的括号才能在整个声明中获得正确的绑定。

这里没有 "transformation" 从尾随 return 类型到前导 return 类型。相反,尾随 return 类型只允许您直接指定 return 类型,而前导 return 类型由递归展开声明符的算法解释。虽然这在 "declaration follows usage" 的原则下是有道理的,但人类(包括非常有经验的 C++ 程序员)往往很难凭直觉掌握。尤其是当我们必须反过来做时(写下声明,而不是解释现有的声明)。