Это является переделка моего ответа на this question
Вашего намерение состоит в том, чтобы иметь все производные классы в иерархии наследует клонируемость (полиморфная копия) от своего базового класса так , что вы также не должны предоставлять каждый из них с переопределением из clone()
, но ваших попыток решения CRTP с шаблоном класса BaseCopyable
может только лишь наделяет клонируемость таким образом на класса es, немедленно полученных от Base
, а не от классов, полученных от таких производных классов.
Я не думаю, что невозможно размножать клонирование вплоть до произвольной глубинной иерархии , предлагая клонирование «всего один раз» на самых лучших конкретных классах. Вы должны явно наделять его на каждый конкретного класса, но вы можете сделать это с помощью своих базовых классов и без repetitiously перекрывая 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
не нужно be виртуальный база cloner<B,D>
и вы можете удалить ключевое слово virtual
из struct cloner : virtual B
. В противном случае, B
должна быть виртуальной базой так, что конструктор B
не по умолчанию может быть вызвана с помощью конструктора D
, хотя B
не является прямой базой D
.
В C++ 11, где мы имеем VARIADIC шаблонов, вы можете обойтись без виртуального наследования в целом путем предоставления cloner<B,D>
с «универсальным» шаблона конструктора, через который он может направить произвольному конструктору аргументов D
в B
, Вот пример того, что:
#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
Вы могли бы просто требовать, что только наиболее производного класса наследуют от 'BaseCopyable'. –
Не является ли целью функции-члена клона, что вы можете вернуть производный тип в переопределение в производном? Это называется «ковариантными типами возврата» и идеально подходит для этой функции. – pmr