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.
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.
Clearly, a CRTP template that fits this bill will differ from BaseCopyable
by requiring two template parameters: the parent type and the child type.
A C++03 solution is as illustrated by the following program:
#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
. 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
.
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
. 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