无论是谁说这个问题是问题“ Is there a specific reason why a trailing-return-type is not a complete-class context of a class?”的重复,都不知道他/她在说什么。尾随返回类型不是类的完整类上下文这一事实不说明了为何此问题中的代码无法编译,尽管它说明了答案中给出的代码被拒绝另一个问题,特别是涉及成员函数qux
和baz
的部分代码,如OP所述。
为了阐明我的论点,以下代码是有效的,您必须考虑[expr.prim.this]中的the second note,它表示:在尾随返回类型中,出于类成员访问的目的,不需要完全定义所定义的类。稍后声明的类成员是不可见的。由于在我的示例中foo
是在bar
之前声明的,因此没有什么可以阻止编译器在尾随返回中访问foo
的情况, bar
的类型。
请注意,@ NathanOliver的the comment below基于这样的猜想,即下面的成员函数 foo
的内联定义只是语法糖。这需要从标准中的引用中得到证明。 我还没有发现。一旦产生了报价,我肯定会接受一个答案,认为该代码无法编译,因为尾随返回类型不是类的完整类上下文。
struct Test {
auto foo() {}
auto bar() -> decltype(foo()) {}
};
prog.cc:3:32: error: use of 'auto Test::foo()' before deduction of 'auto'
3 | auto bar() -> decltype(foo()) {}
| ^
prog.cc:3:32: error: use of 'auto Test::foo()' before deduction of 'auto'
如果具有声明的返回类型的函数使用占位符类型 没有不丢弃的返回语句,返回类型推导为 虽然从return语句的结束括号中没有操作数 功能主体。 [示例:
auto f() { } // OK, return type is void
auto* g() { } // error, cannot deduce auto* from void()
-示例]
包含占位符类型的类型
T
和对应的 初始化程序e
的确定如下:(2.1)表示不丢弃 在用返回类型声明的函数中出现的return语句 包含占位符类型,
T
是声明的返回类型,e
是return语句的操作数。 如果return语句没有 操作数,那么e
是void()
;(2.2)对于用a声明的变量 包含占位符类型的类型,T是声明的类型 变量,e是初始化程序。如果初始化是 直接列表初始化,初始化器应为 仅包含一个赋值表达式和e的braced-init-list 是赋值表达式;
(2.3)用于非类型模板 使用包含占位符类型的类型声明的参数,T为 非类型模板参数的声明类型,e是 相应的模板参数。
根据[dcl.spec.auto] / 9和[dcl.type.auto.deduct] /(2.1),应编译代码。但是GCC和克兰拒绝了它。我想念什么?
答案 0 :(得分:4)
struct Test { auto foo() { /*1*/ } auto bar() -> decltype(foo()) {} };
在标记1处,名称Test::bar
与struct Test
的所有其他成员一起在范围内。因此,编译器无法在类完成之前分析foo()
的正文。
然后我们进行部分排序:
Test::foo()
的返回类型之前先对其进行解析Test
的正文之前,完成类Test::foo()
Test::bar()
之前分析Test
的尾随返回类型(根据您提出的问题不是欺骗)并通过传递性,必须对Test::bar()
的返回类型进行分析,而无需对Test::foo()
进行返回类型推导。
由于请求了标准报价,因此它来自[class.mem]
:
在 class-specifier 的结尾
}
处,类被视为完全定义的对象类型(或完整类型)。在类成员规范中,该类在函数体内被视为完整的,默认参数, noexcept-specifiers 和默认成员初始化程序(包括嵌套类中的此类内容)。否则,在其自己的类成员规范中,它被认为是不完整的。