我偶然发现了这段代码,其中包含尾随返回类型和继承。
以下最小示例可以使用g ++而不是clang进行编译
struct Base {};
int foo(Base&) {
return 42;
}
struct Derived : public Base {
auto bar() -> decltype(foo(*this)) {
return foo(*this);
}
};
int main()
{
Derived derived;
derived.bar();
return 0;
}
但是,如果我们将auto bar() -> decltype(foo(*this))
更改为decltype(auto) bar()
(c ++ 14扩展名),代码也会使用clang进行编译。链接到Godbolt https://godbolt.org/z/qf_k6X。
谁能解释我
auto bar() -> decltype(return expression)
与decltype(auto) bar()
有何区别答案 0 :(得分:5)
这是一个gcc错误,尾随返回类型不在完整类上下文中 [class.mem]
一个类的完整类上下文是
- 功能主体
- 默认参数
- noexcept-specifier([except.spec]),
- 合同条件,或
- 默认成员初始化程序
我们看到从[conv.ptr]
派生到基本的转换需要一个完整的类。“指向Cv D的指针”类型的prvalue(其中D是完整类类型)可以转换为“指向Cv B的指针”类型的prvalue,其中B是基类D。
如果可以通过标准转换序列将“指向cv2 T2的指针”的prvalue转换为“指向cv1 T1的指针”的类型,则“ cv1 T1”与“ cv2 T2”具有参考兼容性。在所有情况下,如果使用两种类型的参照兼容关系来确定参照绑定的有效性,并且标准转换序列的格式不正确,则需要这种绑定的程序就会格式不正确。
另一方面,函数主体位于完整类上下文中,因此,派生到基本的转换格式良好。返回类型涉及占位符类型(decltype(auto)
is valid,只要在使用它的表达式之前已经推断出它即可。
对于C ++ 11中可能的解决方法,您可以使用
auto bar() -> decltype(foo(std::declval<Base&>()))
{
return foo(*this);
}
前提是您知道要使用Base
进行调用。
答案 1 :(得分:1)
我认为Clang拒绝这样做是错误的:
关于函数定义的返回类型,C ++ 14标准对此表示:
类型不得在返回或参数类型中定义。参数的类型或返回类型 除非删除该函数(8.4.3)或该定义嵌套在该类的成员规范内,否则该函数定义不得为不完整的类类型(可能具有cv限定) 在该类中定义的嵌套类中)。
在您的示例中,bar
的定义嵌套在class Derived
的成员规范内。因此,这是允许的,GCC,ICC和MSVC可以做到这一点。
另一方面,decltype(auto)
之所以起作用,是因为直到需要该函数的签名才实际推断出推导的返回类型。
在您的情况下,当您在bar()
中调用main
时会发生这种情况。在那时,class Derived
是一个完全定义的类型。 lang声是正确的。
请注意,即使您使用auto
而不是decltype(auto)
也适用于您的示例。参见Demo的“ godbolt”。