decltype 要求的不完整类型的派生到基转换
Derived-to-base conversion for incomplete types required by decltype
我发现这段代码被剪掉了,涉及尾随 return 类型和继承。
下面的最小示例可以用 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 进行编译。 Link 神马 https://godbolt.org/z/qf_k6X .
谁能给我解释一下
auto bar() -> decltype(return expression)
与 decltype(auto) bar()
有何不同
- 为什么编译器之间的行为不同
- 正确的实施方式是什么?
我认为 Clang 拒绝这个是错误的:
关于函数定义的return类型,C++14标准是这样说的:
Types shall not be defined in return or parameter types. The type of a parameter or the return type for
a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function is deleted (8.4.3) or the definition is nested within the member-specification for that class (including definitions
in nested classes defined within the class).
在您的示例中,bar
的定义嵌套在 class Derived
的成员规范中。所以这是允许的,GCC、ICC 和 MSVC 做对了。
另一方面,decltype(auto)
之所以有效,是因为推导的 return 类型在需要函数签名之前实际上并未推导。
在您的情况下,当您在 main
中调用 bar()
时,就会发生这种情况。那时 class Derived
是一个完全定义的类型。 Clang 做对了。
请注意,即使使用 auto
而不是 decltype(auto)
也适用于您的示例。参见 Godbolt 上的 Demo。
这是一个 gcc 错误,尾随的 return 类型不在 complete-class 上下文中 [class.mem]
A complete-class context of a class is a
- function body,
- default argument,
- noexcept-specifier ([except.spec]),
- contract condition, or
- default member initializer
我们看到从 [conv.ptr]
派生到基础的转换需要一个完整的 class
A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class of D.
“cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence. In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.
另一方面,函数体位于 complete-class context 中,因此派生到基础的转换是良构的。 return 类型涉及占位符类型 (decltype(auto)
) is valid 只要它在使用它的表达式之前已经被推导。
对于 C++11 中可能的解决方法,您可以使用
auto bar() -> decltype(foo(std::declval<Base&>()))
{
return foo(*this);
}
前提是您知道要用 Base
调用它。
我发现这段代码被剪掉了,涉及尾随 return 类型和继承。
下面的最小示例可以用 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 进行编译。 Link 神马 https://godbolt.org/z/qf_k6X .
谁能给我解释一下
auto bar() -> decltype(return expression)
与decltype(auto) bar()
有何不同
- 为什么编译器之间的行为不同
- 正确的实施方式是什么?
我认为 Clang 拒绝这个是错误的:
关于函数定义的return类型,C++14标准是这样说的:
Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function is deleted (8.4.3) or the definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).
在您的示例中,bar
的定义嵌套在 class Derived
的成员规范中。所以这是允许的,GCC、ICC 和 MSVC 做对了。
另一方面,decltype(auto)
之所以有效,是因为推导的 return 类型在需要函数签名之前实际上并未推导。
在您的情况下,当您在 main
中调用 bar()
时,就会发生这种情况。那时 class Derived
是一个完全定义的类型。 Clang 做对了。
请注意,即使使用 auto
而不是 decltype(auto)
也适用于您的示例。参见 Godbolt 上的 Demo。
这是一个 gcc 错误,尾随的 return 类型不在 complete-class 上下文中 [class.mem]
A complete-class context of a class is a
- function body,
- default argument,
- noexcept-specifier ([except.spec]),
- contract condition, or
- default member initializer
我们看到从 [conv.ptr]
派生到基础的转换需要一个完整的 classA prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class of D.
“cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence. In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.
另一方面,函数体位于 complete-class context 中,因此派生到基础的转换是良构的。 return 类型涉及占位符类型 (decltype(auto)
) is valid 只要它在使用它的表达式之前已经被推导。
对于 C++11 中可能的解决方法,您可以使用
auto bar() -> decltype(foo(std::declval<Base&>()))
{
return foo(*this);
}
前提是您知道要用 Base
调用它。