Derived curiously recurring templates and covariance

前端 未结 2 1152
野的像风
野的像风 2021-01-04 20:35

Suppose I have a base class which cloning of derived classes:

class Base
{
    public:
        virtual Base * clone()
        {
            return new Base()         


        
相关标签:
2条回答
  • 2021-01-04 20:59

    If you can live with having to use a different syntax for specifying complete types, you might do the following (warning: untested code):

    Let's first start with the machinery:

    // this gives the complete type which needs to be used to create objects
    // and provides the implementation of clone()
    template<typename T> class Cloneable:
      public T
    {
    public:
      template<typename... U> Cloneable(U&&... u): T(std::forward<U>(u) ...) {}
      T* clone() { return new Cloneable(*this); }
    private:
      // this makes the class complete
      // Note: T:: to make it type dependent, so it can be found despite not yet defined
      typename T::CloneableBase::CloneableKey unlock() {}
    };
    
    // this provides the clone function prototype and also makes sure that only
    // Cloneable<T> can be instantiated
    class CloneableBase
    {
      template<typename T> friend class Cloneable;
    
      // this type is only accessible to Clonerable instances
      struct CloneableKey {};
    
      // this has to be implemented to complete the class; only Cloneable instances can do that
      virtual CloneableKey unlock() = 0;
    public:
      virtual CloneableBase* clone() = 0;
      virtual ~CloneableBase() {}
    };
    

    OK, now the actual class hierarchy. That one is pretty standard; no CRTP intermediates or other complications. However no class implements the clone function, but all inherit the declaration (directly or indirectly) from CloneableBase.

    // Base inherits clone() from CloneableBase
    class Base:
      public CloneableBase
    {
      // ...
    };
    
    // Derived can inherit normally from Base, nothing special here
    class Derived:
      public Base
    {
      // ...
    };
    

    Here's how you then create objects:

    // However, to create new instances, we actually need to use Cloneable<Derived>
    Cloneable<Derived> someObject;
    Derived* ptr = new Cloneable<Derived>(whatever);
    
    // Now we clone the objects
    Derived* clone1 = someObject.clone();
    Derived* clone2 = ptr->clone();
    
    // we can get rid og the objects the usual way:
    delete ptr;
    delete clone1;
    delete clone2;
    

    Note that a Cloneable<Derived> is-a Derived (it is a subclass), therefore you need to use Cloneable only for construction, and can otherwise pretend to work with Derived objects (well, tyepinfo will also identify it as Cloneable<Derived>).

    0 讨论(0)
  • 2021-01-04 21:01

    A not-so-pretty workaround.

    class Base
    {
        protected:
            virtual Base * clone_p()
            {
                return new Base();
            }
    };
    
    
    template <class T>
    class CRTP : public Base
    {
        protected:
            virtual CRTP* clone_p()
            {
                return new T;
            }
        public:
            T* clone()
            {
                CRTP* res = clone_p();
                return static_cast<T*>(res);
            }
    };
    
    
    class Derived : public CRTP<Derived>
    {
        public:
    };
    

    Use dynamic_cast<> instead of static if you feel it's safer.

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