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