Derived-to-base conversion for incomplete types required by decltype
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.