问题
I'm trying to implement a Clonable class with the CRTP. However, I need to have abstract class that have a pure virtual clone method, overridden by child classes. To make this happen, I need the clone function to return a covariant return type. I made this code below, and the compiler shout at me this error:
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 *')
The class 'B' seems to be a child class of AbstractClonable, and even by two way! How can I solve this? Thank you very much. I tried with both with 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> {
};
回答1:
Even if B
is indeed derived from Clonable<B>
, the problem here is that Clonable<B>
construction is not valid, as it defines
B* clone() const override
which of course is not an override of AbstractClonable::clone()
, since the compiler doesn't see B
at this point as a child of AbstractClonable
. So I believe the issue lays in the fact that the compiler cannot build the Clonable<B>
base of B
.
A workaround (but not really the same as what 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: Derived curiously recurring templates and covariance
回答2:
Yes, B
is derived from AbstractClonable
, but the compiler doesn't know that during the instantiation of Clonable<B>
because B
is still incomplete at that point.
C++14 §10.3/8:
If the class type in the covariant return type of
D::f
differs from that ofB::f
, the class type in the return type ofD::f
shall be complete at the point of declaration ofD::f
or shall be the class typeD
.
A class has special permission to use itself in a covariant return type. Other classes, including CRTP bases, need to wait until the class is complete before declaring a covariant function.
You can solve the problem using the non-virtual interface idiom (NVI):
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's OK.
return static_cast< T * >( do_clone() );
}
};
回答3:
I think the problem is that
T* clone() const override{
return new T{*dynamic_cast<const T*>(this)};
}
returns B* instead of AbstractClonable *.
来源:https://stackoverflow.com/questions/30252032/invalid-covariant-type-with-crtp-clonable-class