6

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

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

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

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

我错过了吗?

标准的哪些部分解释了上述尾随返回类型的简化?

4

2 回答 2

4

IMO,你有两个不相关的问题,我会尝试回答第一个。[basic.scope.class]/1.5
涵盖了它:

延伸到或超过类定义结尾的声明的潜在范围也延伸到由其成员定义定义的区域,即使成员是在类外部的词法定义的(这包括静态数据成员定义、嵌套类定义、成员函数定义(包括成员函数体和此类定义的声明部分的任何部分,其跟在declarator-id之后,包括参数声明子句和任何默认参数([dcl.fct.default])。

在成员函数的类外定义中

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

trailing-return-typedeclarator-id 之后X::f,因此它是类成员的潜在范围,因此无限定查找会X::foofoo此处提及时找到。

于 2019-10-19T17:55:04.943 回答
2

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

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

一个普通的前导返回类型在declarator-id之前,即X::f在命名空间范围内查找它。尾随返回类型跟随它,因此在类范围内查找它。

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

-> 类型标识

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

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

在有表格的声明T DD

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

并且声明中包含的declarator-id的类型T D1是“<em>derived-declarator-type-list T”,其中declarator-id的类型D是“<em>derived-declarator-type-list noexcept (opt)(参数声明子句的函数) cv-qualifier-seq (opt) ref-qualifier (opt) 返回T”,其中 ...

这里,T表示一个decl-specifier-seq。如果您有一个表示的typedef-nameint(*)(int),例如FPII,那么您可以使用它:

FPII g(float);

但是,如果您想以艰难的方式做到这一点,我们必须找到TandD1这样,当派生声明符类型列表根据 的句法形式D1施加的类型转换序列,应用于“函数返回" ,结果是"返回指针的函数(返回函数)"。TD1intTfloatintint

如果派生声明符类型列表是“float返回指针的函数”,并且Tint. 因此,声明符D1必须具有语法形式* declarator-id (float)才能产生所述派生声明符类型列表。为了使整个声明中的绑定正确,我们必须添加一对额外的括号。

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

于 2019-11-22T19:04:53.210 回答