Invalid covariant type with CRTP clonable class

前端 未结 3 1913
眼角桃花
眼角桃花 2020-12-20 19:27

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 t

相关标签:
3条回答
  • 2020-12-20 20:05

    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 of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D.

    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() );
        }
    };
    
    0 讨论(0)
  • 2020-12-20 20:16

    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

    0 讨论(0)
  • 2020-12-20 20:21

    I think the problem is that

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

    returns B* instead of AbstractClonable *.

    0 讨论(0)
提交回复
热议问题