I have seen solutions (including in this site) to the problem of having to implement a clone
method in a class, so it return
s a heap-allocated clone of itself even if we only have the Base
class.
The problem come up when you have to implement in a lot of classes the same method again and again, and I remember a way of doing this when you only have to inherit from a template class (name it Cloneable
) so it implements it, but the way i'm trying to do it doesn't work:
template<typename T, typename B> class Cloneable{
public:
B* clone(){return new T(*this);}
};
class Bottom {
public:
virtual void sayhi() = 0;
virtual Bottom* clone() = 0;
};
class Middle: public Cloneable<Middle, Bottom>, public Bottom {
public:
void sayhi() override {cout << "Hi from the Middle" << endl;}
};
//class Top: public Middle ...?
int main() {
Bottom* b1 = new Middle();
b1->sayhi();
delete b1;
}
I remember that the person that showed this to me actually did it with just one argument in Cloneable
, but anyways, i would thank any way that you can imagine of doing this.
CodePudding user response:
You could use the Curious Recurring Template Pattern where a derived class actually derives from an instanciation of it base class depending on itself. Demo:
#include <iostream>
template<typename T>
class Clonable {
public:
T* clone() {
return new T { * static_cast<T *>(this)};
}
};
template<class T>
class Base : public Clonable<T> {
public:
int x = 2;
virtual ~Base() {}
};
class Derived : public Base<Derived> {
public:
int y = 3;
};
int main() {
Base<Derived> *d = new Derived;
static_cast<Derived *>(d)->y = 6;
Derived *c = d.clone(); // we are even cloning from a Base *!
std::cout << c->x << ' ' << c->y << '\n';
delete c;
delete d;
}
It compiles with no warning and correctly outputs 2 6
, proving that no slicing occurred.
CodePudding user response:
Your Cloneable
is missing a cast. this
is a Cloneable *
, it needs to be cast to T *
to be passed to the copy constructor.
template<typename T, typename B> class Cloneable : public B
{
public:
B* clone() override { return new T(*static_cast<T*>(this)); }
};
class Base
{
public:
virtual Base* clone() = 0;
};
class Derived : public Cloneable<Derived, Base>
{
};
This doesn't stop someone from further subclassing Derived
, and forgetting to reimplement clone
for that grandchild class.
class Oops : public Derived {};
int main() {
Oops o;
Base * pb = o.clone();
assert(typeid(*pb) != typeid(o));
}