Here's the thing. While to us it may appear that the compiler knows everything it needs to know about the types in question, the standard says otherwise.
[temp.arg.type/2]
... [ Note: A template type argument may be an incomplete type. — end note ]
[basic.types/5]
A class that has been declared but not defined, an enumeration type in
certain contexts ([dcl.enum]), or an array of unknown bound or of
incomplete element type, is an incompletely-defined object type.46
Incompletely-defined object types and cv void are incomplete types
([basic.fundamental]). Objects shall not be defined to have an
incomplete type.
[class/2]
A class-name is inserted into the scope in which it is declared
immediately after the class-name is seen. The class-name is also
inserted into the scope of the class itself; this is known as the
injected-class-name. For purposes of access checking, the
injected-class-name is treated as if it were a public member name. A
class-specifier is commonly referred to as a class definition. A class
is considered defined after the closing brace of its class-specifier
has been seen even though its member functions are in general not yet
defined. The optional attribute-specifier-seq appertains to the class;
the attributes in the attribute-specifier-seq are thereafter
considered attributes of the class whenever it is named.
The text in bold paints the simple picture that the compilers in question treat the type parameter T as an incomplete object type. It's as though you only forward declared it, like so:
class derived;
They cannot deduce that this forward declaration is a class derived from base
. So they cannot accept it as a co-variant return type in the context of foo_default_impl
. Like was pointed out by @marcinj in the comments:
[class.virtual/8]
If the class type in the covariant return type of D::f differs from
that of B::f, the class type in the return type of D::f shall be
complete at the point of declaration of D::f or shall be the class
type D.
Since T
is neither complete, nor is it foo_default_impl<T>
itself, it cannot be a co-variant return type.