3

I have seen solutions (including in this site) to the problem of having to implement a clone method in a class, so it returns a heap-allocated clone of itself even if we only have the Baseclass.

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.

Jonnas Kaf
  • 67
  • 5
  • 1
    you are using `Middle` before its definition in template parameter and anything that inherits from a abstract class should override the functions in abstract class else the child will be absract as well – IcanCode Jan 20 '22 at 14:24
  • @IcanCode i see, but there has to be a way – Jonnas Kaf Jan 20 '22 at 14:31
  • There is no way to do what you want. First, from `Cloneable` point of view, `*this` is `Cloneable&`, so `new T(*this)` expects a constructor taking a `Cloneable&`, not a `Middle&`, or whatever. Second, having `clone` in `Cloneable` does not "implement" the virtual `clone` from `Bottom`. In languages such as Java, you could do this, but this is not possible in C++. – Holt Jan 20 '22 at 14:46
  • "you have to implement in a lot of classes the same method again and again": why not put them in the base class ? –  Jan 20 '22 at 15:13
  • @YvesDaoust because the base class doesn't know what type to `new` – Caleth Jan 20 '22 at 15:34
  • How is that solved by moving the functions to the derived classes ??? –  Jan 20 '22 at 15:37

2 Answers2

6

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.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
2

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

See it live

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));
}
Caleth
  • 52,200
  • 2
  • 44
  • 75