class A {
virtual A* foo() = 0;
};
template<class T>
class B : public A {
virtual T* foo() { return nullptr; }
};
class C : public B<C> {
};
This is a simplified implementation for Possibility to mix composite pattern and curiously recurring template pattern. I get the following error:
Return type of virtual function 'foo' is not covariant with the return type of the function it overrides ('C *' is not derived from 'A *')
Tested on clang 3.0, gcc 4.7 and visual studio 2008.
First solution:
class C : public A, public B<C> {}
compiles under visual studio with a warning that B is already a child of A and does not compile under clang with initial error.
Another workaround:
class D : public A {}
class C : public B<D> {}
solves the incompleteness issue, but I can't figure out how many A instances will I have. Intuition tells me that A is virtual, thus there should be only one.
Also this workaround creates unreadable code.
What does the standard states about this situation? Should this code compile? If not, why?
Your virtual function A::foo()
returns an A*
, while function B<C>::foo()
, which is meant to override it, returns a C*
.
This in theory does respect the principle of covariance, since C
is indeed a specialization of (derives from) A
, but at the point of instantiation, this is not known, because C
is an incomplete type.
One possible way to re-think your design is to make A
a class template as well, and let B
propagate the template argument for T
up to A
:
template<typename T>
class A {
virtual T* foo() = 0;
};
template<class T>
class B : public A<T> {
virtual T* foo() { return nullptr; }
};
Concerning your workaround:
What does the standard states about this situation? Should this code compile? If not, why?
It shouldn't compile, because the mere fact of making C
also derive from A
explicitly (notice, that you would end up with two distinct base sub-objects of type A
inside C
) does not make C
a complete type when instantiating B<C>
. Per Paragraph 9.2/2 of the C++11 Standard:
A class is considered a completely-defined object type (3.9) (or complete type) at the closing
}
of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, and brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
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