Invalid covariance type with CRTP clone class

I am trying to implement the Clonable class with CRTP. However, I need an abstract class that has a pure virtual cloning method, overridden by child classes. For this to happen, I need a clone function to return a covariant return type. I made this code below, and the compiler shouted this error to me:

main.cpp:12:5: error: return type of virtual function 'clone' is not covariant with the return type of the function it overrides ('B *' is not derived from 'AbstractClonable *') 

Class "B" seems to be a child of AbstractClonable, and even in two ways! How can i solve this? Thank you very much. I tried with both clang 3.6 and GCC 4.9.2

 struct AbstractClonable { virtual AbstractClonable* clone() const = 0; }; template<typename T> struct Clonable : virtual AbstractClonable { T* clone() const override { return new T{*dynamic_cast<const T*>(this)}; } }; struct A : virtual AbstractClonable { }; struct B : A, Clonable<B> { }; 
+6
source share
3 answers

Even if B indeed derived from Clonable<B> , the problem here is that the Clonable<B> construct is invalid because it defines

 B* clone() const override 

which, of course, is not an override of AbstractClonable::clone() , since the compiler does not see B at this point as a child of AbstractClonable . Therefore, I believe that the problem is that the compiler cannot build the Clonable<B> B base.

The workaround (but not quite the same as you want) is to define

 Clonable* clone() const override 

in Clonable . As you mentioned in the comment, you can also define a free function

 template<typename T> T* clone(const T* object) { return static_cast<T*>(object->clone()); } 

Related: It turned out curiously repeating patterns and covariance

+3
source

Yes, B derived from AbstractClonable , but the compiler does not know what the Clonable<B> at the time of creation, since B is still not complete at this point.

C ++ 14 ยง10.3 / 8:

If the class type in the covariant return type D::f is different from the type B::f , the class type in the return type D::f must be complete at the point of declaration D::f or it must be a class type D

The class has special permission to use itself in the covariant return type. Other classes, including CRTP bases, must wait for the class to finish before declaring a covariant function.

You can solve the problem using the non-virtual interface (NVI) idiom:

 class AbstractClonable { protected: virtual AbstractClonable* do_clone() const = 0; public: AbstractClonable *clone() const { return do_clone(); } }; template<typename T> class Clonable : public virtual AbstractClonable { Clonable* do_clone() const override { // Avoid using T in this declaration. return new T{*dynamic_cast<const T*>(this)}; } public: T *clone() const { // But here, it OK. return static_cast< T * >( do_clone() ); } }; 
+3
source

I think the problem is that

 T* clone() const override{ return new T{*dynamic_cast<const T*>(this)}; } 

returns B * instead of AbstractClonable *.

0
source

Source: https://habr.com/ru/post/987245/


All Articles