This (if I understand the question correctly) is known as ¹cloning. Just add a virtual clone
member function in the base class. Override it in every concrete derived class.
Example of the core functionality:
class Base
{
private:
// Whatever
public:
virtual auto clone() const
-> Base*
{ return new Base( *this ); }
virtual ~Base() {}
};
class Derived_A
: public Base
{
public:
auto clone() const
-> Derived_A* // OK, covariant return type.
override
{ return new Derived_A( *this ); }
};
#include <assert.h>
#include <typeinfo>
auto main()
-> int
{
Base const& o = Derived_A{};
auto p = o.clone();
assert( typeid( *p ) == typeid( Derived_A ) );
delete p; // ← Manual cleanup is a problem with basic cloning.
}
Overriding the clone
function with a function that returns Derived_A*
instead of general Base*
is OK because it's a raw pointer and the result type is covariant (more specific in the more specific class, i.e. varies in the same way as the class specificity). It would also work nicely also for a raw reference. But C++ does not directly support this for class type function results, and that includes smart pointers as clone
function results.
As the comment indicates, one problem with the direct, simple cloning is that the cleanup responsibility is unclear. It would be better to have that automated and guaranteed, but then one runs into the no-support-for-covariant-smart-pointers problem. Happily that covariance can be implemented “manually”, by making the virtual clone
function non-public
, and providing a class-specific smart pointer result wrapper function in every class.
Example of cloning with covariant smart pointer result:
#include <memory> // std::unique_ptr
class Base
{
private:
// Whatever, and
virtual auto virtual_clone() const
-> Base*
{ return new Base( *this ); }
public:
auto clone() const
{ return std::unique_ptr<Base>( virtual_clone() ); }
virtual ~Base() {}
};
class Derived_A
: public Base
{
private:
auto virtual_clone() const
-> Derived_A* // OK, covariant return type.
override
{ return new Derived_A( *this ); }
public:
auto clone() const
{ return std::unique_ptr<Derived_A>( virtual_clone() ); }
};
#include <assert.h>
#include <typeinfo>
auto main()
-> int
{
Base const& o = Derived_A{};
auto p = o.clone();
assert( typeid( *p ) == typeid( Derived_A ) );
// Automatic cleanup.
}
Here is one way to automatically generate the cloning support machinery in Base
and Derived_A
, based on the notion of middle-man inheritance:
#include <memory> // std::unique_ptr
#include <utility> // std::forward
// Machinery:
template< class Derived_t >
class With_base_cloning_
{
private:
auto virtual virtual_clone() const
-> Derived_t*
{ return new Derived_t( *static_cast<Derived_t const*>( this ) ); }
public:
auto clone() const
{ return std::unique_ptr<Derived_t>( virtual_clone() ); }
virtual ~With_base_cloning_() {}
};
template< class Derived_t, class Base_t >
class With_cloning_
: public Base_t
{
private:
auto virtual_clone() const
-> Base_t* // Ungood type because Derived_t is incomplete here.
override
{ return new Derived_t( *static_cast<Derived_t const*>( this ) ); }
public:
auto clone() const
{ return std::unique_ptr<Derived_t>( static_cast<Derived_t*>( virtual_clone() ) ); }
template< class... Args >
With_cloning_( Args... args )
: Base_t( std::forward<Args>( args )... )
{}
};
And you'd use it like this:
// Usage example:
class My_base
: public With_base_cloning_<My_base>
{};
class Derived_A
: public With_cloning_<Derived_A, My_base>
{};
#include <assert.h>
#include <typeinfo>
auto main()
-> int
{
My_base const& o = Derived_A{};
auto p = o.clone();
assert( typeid( *p ) == typeid( Derived_A ) );
// Automatic cleanup.
}
The return type of the (private) virtual_clone
function in With_cloning_
is not directly what one desires, not ideal, because the derived class is not yet complete at the point where the template is instantiated, so the compiler doesn't yet know that it's derived from the template instantiation.
Alternatives to this middle-man inheritance solution, include a simple code generation macro, and (complex) dominance in a virtual inheritance hierarchy.
¹ A clone
function, at bottom invoking the most derived class' copy constructor, is one special case of the virtual constructor idiom. Another special case is a create
function, which at bottom invokes the most derived class' default constructor.