CRTP compilation error and dynamic polymorphism

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 the ability to combine a compound template and a curiously repeating template template . 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 decision:

 class C : public A, public B<C> {} 

compiles in visual studio with a warning that B is already a child of A and does not compile under clang with an initial error.

Another workaround:

 class D : public A {} class C : public B<D> {} 

solves the problem of incompleteness, but I can not understand how many copies I have. Intuition tells me that A is virtual, so there should be only one.

Also this workaround creates unreadable code.

What does the standard say about this situation? Should this code compile? If not, why?

+6
source share
1 answer

Your virtual function A::foo() returns A* , and the function B<C>::foo() , which is intended to be overridden, returns C* .

This theoretically respects the principle of covariance, since C really is a specialization (comes from) A , but at the time of instantiation this is not known because C is an incomplete type.

One possible way to reconsider your design is to make A a class template, and also B pass the template argument for T to A :

 template<typename T> class A { virtual T* foo() = 0; }; template<class T> class B : public A<T> { virtual T* foo() { return nullptr; } }; 

Regarding your workaround:

What does the standard say about this situation? Should this code compile? If not, why?

It should not be compiled, because the very fact of creating C also derived from A explicitly (note that in the end you get two different base sub-objects of type A inside C ) it does not make C full type when creating an instance of B<C> . In paragraph 9.2 / 2 of the C ++ 11 standard:

A class is considered a fully defined type of object (3.9) (or a full type) when closing } the class specifier. Within a class-class, a class is considered complete in functional bodies, default arguments, and copied or equal initializers for non-static data members (including such things in nested classes). Otherwise, it is considered incomplete in its class specification.

+4
source

All Articles