Derived curiously recurring templates and covariance

独自空忆成欢 提交于 2019-11-30 03:25:01

问题


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

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

        // ...
};

I have a set of derived classes which are implemented using a curiously recurring template pattern:

template <class T>
class CRTP : public Base
{
    public:
        virtual T * clone()
        {
            return new T();
        }

        // ...
};

And I attempt to derive from that further like this:

class Derived : public CRTP<Derived>
{
    public:
        // ...
};

I get compilation errors to the effect of:

error C2555: 'CRTP<T>::clone': overriding virtual function return type differs and is not covariant from 'Base::clone'

I realize this is probably a result of the compiler not fully knowing the inheritance tree for Derived when instantiating CRTP. Furthermore, replacing the return type (T*) with (Base*) also compiles. However, I would like to know if there is a work around which retains the above semantics.


回答1:


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.




回答2:


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>).



来源:https://stackoverflow.com/questions/17201268/derived-curiously-recurring-templates-and-covariance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!