I have several classes that need the following clone
function to be defined:
struct Base
{
virtual Base * clone() const = 0;
};
struct A : public Base
{
Base * clone() const {
return new A(*this);
}
};
struct B : public Base
{
Base * clone() const {
return new B(*this);
}
};
struct X : public Base2
{
Base2 * clone() const {
return new X(*this);
}
};
I am trying to do this with a Cloneable mixin to avoid this redundant code:
template <typename BASE, typename TYPE>
class CloneableMixin
{
public:
BASE*clone() const {
return new TYPE( dynamic_cast<const TYPE &>(*this) );
}
};
struct A : public Base, public CloneableMixin<Base, A>
{
};
However, this doesn't work, because in new TYPE(*this)
from CloneableMixin
, *this
is of type CloneableMixin<BASE, TYPE>
.
Update: the CloneableMixin
can dynamic_cast
to the correct type. But now I have another problem: CloneableMixin::clone
doesn't successfully override Base::clone
, and so the compiler reports A is a abstract type.
Can some clever use of virtual
inheritance allow CloneableMixin::clone
to override Base::clone
? Is there some macro I should use for this?
Do you know of a way around all of this redundant code?
Can some clever use of virtual inheritance allow CloneableMixin::clone to override Base::clone?
Your CloneableMixin<Base,Derived>
cannot override any method of Base
- either
polymorphically or by hiding - because CloneableMixin<Base,Derived>
is
not derived from Base
.
On the other hand, if CloneableMixin<Base,Derived>
were derived from Base
you would no longer have any need for it to be a mixin, because -
class Derived : public CloneableMixin<Base,Derived> {....};
would inherit Base
.
So for the needs of your example the solution illustrated here will suffice:
#include <iostream>
// cloner v1.0
template <class Base, class Derived>
struct cloner : Base
{
Base *clone() const override {
return new Derived( dynamic_cast<const Derived &>(*this) );
}
~cloner() override {};
};
struct Base
{
virtual Base * clone() const = 0;
Base() {
std::cout << "Base()" << std::endl;
}
virtual ~Base() {
std::cout << "~Base()" << std::endl;
}
};
struct A : cloner<Base,A>
{
A() {
std::cout << "A()" << std::endl;
}
~A() override {
std::cout << "~A()" << std::endl;
}
};
int main()
{
A a;
Base * pb = a.clone();
delete pb;
}
(If you are compiling to the C++03 standard rather than C++11, then you may
simply delete the occurrences of the override
keyword.)
This solution will break down for some more real-worldly class hierarchies, e.g. in this illustration of the Template Method Pattern:
#include <iostream>
#include <memory>
using namespace std;
// cloner v1.0
template<class B, class D>
struct cloner : B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
};
/* Abstract base class `abstract` keeps the state for all derivatives
and has some pure virtual methods. It has some non-default
constructors.
*/
struct abstract
{
virtual ~abstract() {
cout << "~abstract()" << endl;
}
int get_state() const {
return _state;
}
void run() {
cout << "abstract::run()" << endl;
a_root_method();
another_root_method();
}
virtual void a_root_method() = 0;
virtual void another_root_method() = 0;
virtual abstract * clone() const = 0;
protected:
abstract()
: _state(0) {
cout << "abstract(): state = " << get_state() << endl;
}
explicit abstract(int state) : _state(state) {
cout << "abstract(" << state << ") : state = "
<< get_state() << endl;
}
int _state;
};
/* Concrete class `concrete` inherits `abstract`
and implements the pure virtual methods.
It echoes the constructors of `abstract`. Since `concrete`
is concrete, it requires cloneability.
*/
struct concrete : cloner<abstract,concrete>
{
concrete() {
cout << "concrete(): state = " << get_state() << endl;
}
explicit concrete(int state) : abstract(state) { //<- Barf!
cout << "concrete(" << state << ") : state = "
<< get_state() << endl;
}
~concrete() override {
cout << "~concrete()" << endl;
}
void a_root_method() override {
++_state;
cout << "concrete::a_root_method() : state = "
<< get_state() << endl;
}
void another_root_method() override {
--_state;
cout << "concrete::another_root_method() : state = "
<< get_state() << endl;
}
};
int main(int argc, char **argv)
{
concrete c1;
unique_ptr<abstract> pr(new concrete(c1));
pr->a_root_method();
pr->another_root_method();
unique_ptr<abstract> pr1(pr->clone());
pr1->a_root_method();
return 0;
}
When we attempt to build this, the compiler will give an error at
the initialization abstract(state)
in the constuctor of concrete
(at the Barf!
comment), saying:
error: type 'abstract' is not a direct or virtual base of 'concrete'
or words to that effect. Indeed, the direct base of concrete
is not abstract
but cloner<abstract,concrete>
. However, we cannot rewrite the constructor as:
/*Plan B*/ explicit concrete(int state) : cloner<abstract,concrete>(state){....}
Because there is has no such constructor as
cloner<abstract,concrete>::cloner<abstract,concrete>(int)
But the compiler's diagnostic suggests a fix. This is were virtual
inheritance can help. We need abstract
to become a virtual base of concrete
, which
means effectively "an honorary direct base of concrete
", and we can achieve that
just by making B
a virtual base of cloner<B,D>
:
// cloner v1.1
template<class B, class D>
struct cloner : virtual B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
};
With that, we have a clean build and output:
abstract(): state = 0
concrete(): state = 0
concrete::a_root_method() : state = 1
concrete::another_root_method() : state = 0
concrete::a_root_method() : state = 1
~concrete()
~abstract()
~concrete()
~abstract()
~concrete()
~abstract()
There are good reasons to be wary of virtual inheritance on principle and to reserve its use for at least for cases in which it has architectural rationale - not for workarounds, as we have used it just now.
If we prefer to do without virtual inheritance for this problem, then we
must somehow ensure that there is a constructor of cloner<B,D>
that
echoes any constuctor of B
, for arbitrary B
. Then any corresponding
constructor of D
will be able to initialize its direct base cloner<B,D>
whatever the arguments are.
This is a pipedream for C++03, but with the magic of variadic template parameters in C++11 it is easy:
// cloner v1.2
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...){}
};
With this, we can rewrite the concrete
constructor as /*Plan B*/
, and
again we have a correct build and executable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With