Why does the first example work (return type deduction)?
The definition of a member function of a class template is only implicitly instantiated when odr-used (or explicitly instantiated). That is, by deriving from Base<Derived>
, you do not implicitly instantiate the function body. Hence, the return type is still not deduced yet.
At the (*) point of instantiation, Derived
is complete, Derived::foo_impl
is declared, and the return type deduction can succeed.
(*) not "the", but "certain points of instantiation". There are several.
Why doesn't the second example work (trailing-return-type)?
I assumed that the return type from
Base<Derived>::foo
was adecltype
of the expression returned, but if I modify the functionfoo
like this:
The trailing-return-type is part of the declaration of the member function; hence, it is part of the definition of the surrounding class, which is required to be instantiated when deriving from Base<Derived>
. At this point, Derived
is still incomplete, specifically Derived::foo_impl
has not been declared yet.
What could I possibly write to get the correct return type without relying on automatic return type deduction?
Now this is tricky. I'd say this is not very clearly defined in the Standard, e.g. see this question.
Here's an example that demonstrates that clang++3.4 does not find the members of Derived
inside Base<Derived>
:
template<typename Derived>
struct Base
{
auto foo() -> decltype( std::declval<Derived&>().foo_impl() )
{
return static_cast<Derived*>(this)->foo_impl();
}
};
declval
doesn't require a complete type, so the error message is that there's no foo_impl
in Derived
.
There's a hack, but I'm not sure if it's compliant:
template<typename Derived>
struct Base
{
template<class C = Derived>
auto foo() -> decltype( static_cast<C*>(this)->foo_impl() )
{
static_assert(std::is_same<C, Derived>{}, "you broke my hack :(");
return static_cast<Derived*>(this)->foo_impl();
}
};