The typical pattern when you want to copy a polymorphic class is adding a virtual clone method and implement it in each derived class like this:
Base* Derive
You can use this generic CRTP code
template <class Derived, class Base>
struct Clonable : Base {
virtual Base* do_clone() {
return new Derived(*static_cast<Derived*>(this));
}
Derived* clone() { // not virtual
return static_cast<Derived*>(do_clone());
}
using Base::Base;
};
struct empty {};
struct A : Clonable<A, empty> {};
struct B : Clonable<B, A> {};
It can be generalised to smart pointers and multiple bases if desired.
You could use CRTP
to add an additional layer between your derived class and base that implements your cloning method.
struct Base {
virtual ~Base() = default;
virtual Base* clone() = 0;
};
template <typename T>
struct Base_with_clone : Base {
Base* clone() {
return new T(*this);
}
};
struct Derived : Base_with_clone<Derived> {};
If you can control how you pass around the polymorphic type, use type erasure. In particular, the proposed std::polymorphic_value calls the derived copy constructor when it is copied. You can imagine it as something like this:
template <typename B>
class polymorphic_value {
public:
template <typename D,
std::enable_if_t<
std::is_base_of<B, std::decay_t<D>>::value, int> = 0>
explicit polymorphic_value(D&& value)
: ptr{std::make_unique<derived_t<std::decay_t<D>>>(std::forward<D>(value))}
{}
polymorphic_value(polymorphic_value const& rhs)
: ptr{rhs.ptr->clone()}
{}
B const& get() const { return ptr->get(); }
B& get() {
// Safe usage of const_cast, since the actual object is not const:
return const_cast<B&>(ptr->get());
}
private:
struct base_t {
virtual ~base_t() = default;
virtual std::unique_ptr<B> clone() const = 0;
// With more effort, this doesn't have to be a virtual function.
// For example, rolling our own vtables would make that possible.
virtual B const& get() const = 0;
};
template <typename D>
struct derived_t final : public base_t {
explicit derived_t(D const& d)
: value{d}
{}
explicit derived_t(D&& d)
: value{std::move(d)}
{}
std::unique_ptr<B> clone() const override {
return std::make_unique<D>(value);
}
B const& get() const override {
return value;
}
D value;
};
std::unique_ptr<base_t> ptr;
};
For a thorough implementation which follows the proposal, see jbcoe's github repository.
Sample usage:
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
Derived() = default;
Derived(Derived const&);
};
int main() {
polymorphic_value<Base> it{Derived{}};
auto const copy = it;
}
Live on Godbolt
You could use a CRTP approach, but that has other drawbacks:
struct Base {
virtual Base* clone() const = 0;
};
template <typename Derived>
class BaseT : public Base {
// ...
public:
Base* clone() const override {
return new Derived(*static_cast<Derived*>(this));
}
};
Usage:
class DerivedA : public BaseT<DerivedA> {
};
Base *x = new DerivedA();
Base *y = x->clone();
I haven't keep track with the new features in recent C++ standards... Is there a way to avoid this in modern C++?
This trick is available since the c++98 standard.
You probably have a class where you store the polymorphic object and where you want to clone? Together with your polymorphic object you could store a function-pointer doing the cloning:
template<class Derived>
Base* clone(const Base* b) {
return new Derived(static_cast<const Derived*>(b));
}
void SampleUsage() {
Base* b = new Derived;
Base*(*cloner)(const Base*) = clone<Derived>;
Base* copy = cloner(b);
}
The type of cloner
is independent of Derived. Its like a simplified std::function.
You can at minimum avoid writing the class name by getting it from the type of the class itself with:
struct A: public Base
{
Base* Clone() { return new std::remove_reference_t<decltype(*this)>(*this); }
};
Using CRTP will not help avoid duplicating the class name again, as you than have to write the class name inside the template parms for the CRTP base.