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

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

本文关键字:C++ 拷贝 继承 多态 模式      更新时间:2023-10-16

我正在使用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...

但是,如果我有一个从B继承的类,例如:

class differentB : public B;

那么clone()不会返回differentB类型的对象,而是返回一个B。除了在different B中编写一个新的clone(()方法外,有什么方法可以解决这个问题吗?

感谢阅读!

这是我对这个问题的回答的一个返工

您的意图是在层次结构中拥有所有派生类从基类so继承可克隆性(多态拷贝)您不需要同时为它们中的每一个提供覆盖的clone(),但您尝试使用类模板的CRTP解决方案BaseCopyable只能以这种方式赋予直接从Base派生的类,而不是在派生的类上来自这样的派生类。

我认为不可能直接在通过在最高级的混凝土类。你必须明确地授予每个人具体类,但可以通过它们的基类和在不重复覆盖clone()的情况下,通过使用CRTP将中父类的可克隆性传递给子类的模板等级制度

显然,符合该法案的CRTP模板与BaseCopyable不同通过需要两个模板参数:父类型和子类型。

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;
}

输出为:

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

假设所有涉及的类都是默认可构造的,B不需要是cloner<B,D>虚拟基础,并且可以删除virtual来自CCD_ 9的关键字。否则,B必须是虚拟基础使得CCD_ 11的非默认构造函数可以由CCD_,虽然CCD_ 13不是CCD_。

在C++11中,我们有可变模板,你可以不使用虚拟模板为cloner<B,D>提供"全能型"模板构造函数,通过它可以转发任意构造函数从CCD_ 16到CCD_。这是一个例子:

#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;
}

输出为:

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

您可以做的是通过整个继承来传播基等级制度,但我认为这对现在,每进一步派生一个类,都会得到一个全新的层次结构多态性将是徒劳的。

#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> {
};

尽管这仍然有坏的返回类型的缺点CCD_ 18。使用经典的virtual constructor习语,您可以得到说类似的话的能力

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

这在你的计划中是不可能的,尽管很容易实现通过调整CCD_ 20中的返回类型。

只需编写一个宏来摆脱样板:)