简体   繁体   English

奇怪的重复模板模式多态拷贝(C ++)中的继承

[英]Inheritance in curiously recurring template pattern polymorphic copy (C++)

I'm using CRTP to add a clone method to inherited classes, for example: 我正在使用CRTP向继承的类添加克隆方法,例如:

class Base 
{
     virtual ~Base() {};
     virtual Base* clone() const = 0;
}; 

template<class Derived> class BaseCopyable : Base
{ 
public:
    virtual Base* clone() const
    {
        return new Derived(static_cast<Derived const&>(*this));
    }
};

class A : public BaseCopyable<A>;
class B : public BaseCopyable<B>;
etc...

But if I have a class that inherits from B, for example: 但是如果我有一个继承自B的类,例如:

class differentB : public B;

Then clone() doesn't return an object of type differentB, it returns a B. Besides writing a new clone() method in differentB, is there some way to fix this? 然后clone()不返回类型为differentB的对象,它返回一个B.除了在differentB中编写一个新的clone()方法,有没有办法解决这个问题?

Thanks for reading! 谢谢阅读!

This is a rework of my answer to this question 这是我对这个问题的回答的修改

Your intent is to have all the derived classes in your hierarchy inherit cloneability (polymorphic copy) from their base class so that you do not also need to provide each of them with an override of clone() , but your attempted CRTP solution with class template BaseCopyable can only only confer cloneability in this way upon classes immediately derived from Base , and not upon classes derived from such derived classes. 您的目的是让层次结构中的所有派生类从其基类继承可克隆性(多态复制),这样您就不需要为每个类提供clone()的覆盖,但是您尝试使用类模板的CRTP解决方案BaseCopyable只能以这种方式在从Base直接派生的类上赋予可克隆性,而不能在从这些派生类派生的类上赋予克隆性。

I do not think it is not possible to propagate cloneability right down an arbitrarily deep hierarchy by confering cloneability "just once" at the topmost concrete classes. 我认为不可能通过在最顶层的具体类中“仅一次”赋予可克隆性来将克隆性传播到任意深层次结构中。 You must explicitly confer it on each concrete class, but you can do this via their base classes and without repetitiously overriding clone() , by using a CRTP template that relays cloneability from parent class to child in the hierarchy. 您必须在每个具体类上明确赋予它,但您可以通过其基类执行此操作,而无需重复覆盖clone() ,方法是使用CRTP模板将可克隆性从父类传递到层次结构中的子级。

Clearly, a CRTP template that fits this bill will differ from BaseCopyable by requiring two template parameters: the parent type and the child type. 显然,符合此法案的CRTP模板与BaseCopyable的不同之处BaseCopyable需要两个模板参数:父类型和子类型。

A C++03 solution is as illustrated by the following program: C ++ 03解决方案如以下程序所示:

#include <iostream>

// As base of D, this makes D inherit B and makes D cloneable to
// a polymorphic pointer to B
template<class B, class D>
struct cloner : virtual B
{
    virtual B *clone() const {
        return new D(dynamic_cast<D const&>(*this));
    }
    virtual ~cloner() {}       
};

struct Base 
{
    virtual ~Base() {
         std::cout << "I was a Base" << std::endl;
    };
    virtual Base* clone() const = 0;
}; 

struct A : cloner<Base,A> // A inherits Base
{
    virtual ~A() {
         std::cout << "I was an A" << std::endl;
    };
};

struct B : cloner<Base,B> // B inherits Base
{
    virtual ~B() {
         std::cout << "I was a B" << std::endl;
    };
};

struct DB : cloner<B,DB> // DB inherits B, Base
{
    virtual ~DB() {
         std::cout << "I was a DB" << std::endl;
    };
};

int main()
{
    Base * pBaseA = new A;
    Base * pBaseB = new B;
    Base * pBaseDB = new DB;
    Base * pBaseCloneOfA = pBaseA->clone();
    Base * pBaseCloneOfB = pBaseB->clone();
    Base *pBaseCloneOfDB = pBaseDB->clone();
    B * pBCloneOfDB = dynamic_cast<B*>(pBaseDB->clone());
    std::cout << "deleting pBaseA" << std::endl; 
    delete pBaseA;
    std::cout << "deleting pBaseB" << std::endl;
    delete pBaseB;
    std::cout << "deleting pBaseDB" << std::endl;
    delete pBaseDB;
    std::cout << "deleting pBaseCloneOfA" << std::endl;
    delete pBaseCloneOfA;
    std::cout << "deleting pBaseCloneOfB" << std::endl; 
    delete pBaseCloneOfB;
    std::cout << "deleting pBaseCloneOfDB" << std::endl;    
    delete pBaseCloneOfDB;
    std::cout << "deleting pBCloneOfDB" << std::endl;   
    delete pBCloneOfDB;
    return 0;
}

The output is: 输出是:

deleting pBaseA
I was an A
I was a Base
deleting pBaseB
I was a B
I was a Base
deleting pBaseDB
I was a DB
I was a B
I was a Base
deleting pBaseCloneOfA
I was an A
I was a Base
deleting pBaseCloneOfB
I was a B
I was a Base
deleting pBaseCloneOfDB
I was a DB
I was a B
I was a Base
deleting pBCloneOfDB
I was a DB
I was a B
I was a Base

Provided that all the classes involved are default constructible, B need not be a virtual base of cloner<B,D> and you can remove the virtual keyword from struct cloner : virtual B . 如果涉及的所有类都是默认构造的,则B不必是cloner<B,D>虚拟基础,您可以从struct cloner : virtual B删除virtual关键字。 Otherwise, B must be a virtual base so that a non-default constructor of B can be called by a constructor of D , although B is not a direct base of D . 否则, B必须是一个虚基使得非默认构造B可以通过的一个构造被称为D ,虽然B不是直接基D

In C++11, where we have variadic templates, you can do without virtual inheritance altogether by furnishing cloner<B,D> with an "all-purpose" template constructor through which it can forward arbitrary constructor arguments from D to B . 在C ++ 11中,我们有可变参数模板,你可以通过为一个“通用”模板构造函数提供cloner<B,D>完全没有虚拟继承cloner<B,D>通过它可以将任意构造函数参数从D转发到B Here is an illustration of that: 这是一个例子:

#include <iostream>

template<class B, class D>
struct cloner : B
{
    B *clone() const override {
        return new D(dynamic_cast<D const&>(*this));
    }
    ~cloner() override {}
    // "All purpose constructor"
    template<typename... Args>
    explicit cloner(Args... args)
    : B(args...){}  
};

struct Base 
{
    explicit Base(int i)
    : _i(i){}   
    virtual ~Base() {
         std::cout << "I was a Base storing " << _i << std::endl;
    };
    virtual Base* clone() const = 0;
protected:
    int _i;
}; 

struct A : cloner<Base,A>
{
    explicit A(int i)
    : cloner<Base,A>(i){}
    ~A() override {
         std::cout << "I was an A storing " << _i << std::endl;
    };
};

struct B : cloner<Base,B>
{
    explicit B(int i)
    : cloner<Base,B>(i){}
    ~B() override {
         std::cout << "I was a B storing " << _i << std::endl;
    };
};

struct DB : cloner<B,DB>
{
    explicit DB(int i)
    : cloner<B,DB>(i){}
    ~DB() override {
         std::cout << "I was a DB storing " << _i << std::endl;
    };
};

int main()
{
    Base * pBaseA = new A(1);
    Base * pBaseB = new B(2);
    Base * pBaseDB = new DB(3);
    Base * pBaseCloneOfA = pBaseA->clone();
    Base * pBaseCloneOfB = pBaseB->clone();
    Base * pBaseCloneOfDB = pBaseDB->clone();
    B * pBCloneOfDB = dynamic_cast<B*>(pBaseDB->clone());
    std::cout << "deleting pA" << std::endl; 
    delete pBaseA;
    std::cout << "deleting pB" << std::endl;
    delete pBaseB;
    std::cout << "deleting pDB" << std::endl;
    delete pBaseDB;
    std::cout << "deleting pBaseCloneOfA" << std::endl;
    delete pBaseCloneOfA;
    std::cout << "deleting pBaseCloneOfB" << std::endl; 
    delete pBaseCloneOfB;
    std::cout << "deleting pBaseCloneOfDB" << std::endl;    
    delete pBaseCloneOfDB;
    std::cout << "deleting pBCloneOfDB" << std::endl;   
    delete pBCloneOfDB;
    return 0;
}

And the output is: 输出是:

deleting pA
I was an A storing 1
I was a Base storing 1
deleting pB
I was a B storing 2
I was a Base storing 2
deleting pDB
I was a DB storing 3
I was a B storing 3
I was a Base storing 3
deleting pBaseCloneOfA
I was an A storing 1
I was a Base storing 1
deleting pBaseCloneOfB
I was a B storing 2
I was a Base storing 2
deleting pBaseCloneOfDB
I was a DB storing 3
I was a B storing 3
I was a Base storing 3
deleting pBCloneOfDB
I was a DB storing 3
I was a B storing 3
I was a Base storing 3

What you can do is to propagate the base through the whole inheritance hierarchy, but I don't think this will be particularly useful as for every further derived class you now get a whole new hierarchy and all the polymorphism is going to be for naught. 你可以做的是通过整个继承层次传播基础,但是我认为这不会特别有用,因为对于每个进一步的派生类,你现在得到一个全新的层次结构,所有的多态性都将是徒劳的。

#include <iostream>

class Base 
{
public:
     virtual ~Base() {};
     virtual Base* clone() const = 0;
}; 

template<class Derived> class BaseCopyable : Base
{ 
public:
    virtual Base* clone() const
    {
        return new Derived(static_cast<Derived const&>(*this));
    }
};

struct Default;

template<typename Self, typename Arg>
struct SelfOrArg {
  typedef Arg type;
};

template<typename Self>
struct SelfOrArg<Self, Default> {
  typedef Self type;
};

template<typename Derived = Default>
class A : public BaseCopyable< typename SelfOrArg<A<Derived>, Derived>::type >
{

};

class derivedA : A<derivedA> {

};

Although this still has the drawback of the broken return type for BaseCopyable . 虽然这仍然具有BaseCopyable的破坏返回类型的BaseCopyable With a classic virtual constructor idiom, you get the ability to say something like: 使用经典的virtual constructor ,您可以说出如下内容:

void func(Derived& d) {
  // thanks to covariant return types Derived::clone returns a Derived*
  Derived* d2 = d.clone();
  delete d2;
}

This wont be possible with your scheme, although easily possible through adjusting the return type in BaseCopyable . 虽然通过调整BaseCopyable的返回类型很容易实现,但您的方案无法实现。

Just write a macro to get rid of the boilerplate :) 只需写一个宏来摆脱样板:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM