I came across this code snipped involving trailing return types and inheritance.
The following minimal example compiles fine with g++, not with 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;
}
However, if we change auto bar() -> decltype(foo(*this))
to decltype(auto) bar()
(c++14 extension), the code compiles also with clang. Link to godbolt https://godbolt.org/z/qf_k6X .
Can anyone explain me
auto bar() -> decltype(return expression)
differs from decltype(auto) bar()
This is a gcc bug, the trailing return type isn't within a complete-class context [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
We see that a complete class is needed for the derived to base conversion from [conv.ptr]
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.
and [dcl.init.ref]
“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.
On the other hand, a function body is within a complete-class context and thus the derived to base conversion is well-formed. The return type involving a placeholder type (decltype(auto)
) is valid as long as it is already deduced before an expression using it.
For a possible workaround in C++11, you may use
auto bar() -> decltype(foo(std::declval<Base&>()))
{
return foo(*this);
}
provided you know that you want to call it with Base
.
I think Clang is wrong to reject this:
Regarding the return type of a function definition, the C++14 standard says this:
[dcl.fct]/9]
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).
In your example the definition of bar
is nested within the member-specification of the class Derived
. So this is allowed and GCC, ICC and MSVC get this right.
On the other hand decltype(auto)
works because a deduced return type is not actually deduced until the signature of the function is needed.
And in your case, this happens when you call bar()
in main
. At that point of time the class Derived
is a completely defined type. Clang gets this right.
Note that even using auto
instead of decltype(auto)
will work for your example. See Demo on godbolt.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With