c++design-patternscrtpcloneable

Improving safety of Clone pattern


If one wants to implement Clone pattern in C++, he might not be sure about safety, because derived class may forget to override it:

struct A {
    virtual A* Clone() const {
        return new A(*this);
    }
}

struct B : A {
    int value;
};

int main() {
   B b;
   // oops
   auto b_clone = b.Clone();
   delete b_clone;
}

What are the possible ways to improve Clone pattern in C++ in this regard?

A more general question has been asked: Forcing a derived class to overload a virtual method in a non-abstract base class

However, it seems to be too general to have a good solution in C++ -- the discussion is about possible ways to enforce method override. I'm more interested in discovering a useful pattern, which might help in the exact case of using Cloneable pattern.


Solution

  • This is an elaboration of one of the answers, suggesting runtime check using typeid: Forcing a derived class to overload a virtual method in a non-abstract base class

    Using CRTP, one can come up with the following basic idea:

    Create class Cloneable<Derived>, which manages cloning for Derived, and adds all the needed runtime checks (seems like that compile-time checks are not possible even with CRTP).

    However, it is not trivial, and one also has to manage inheritance through Cloneable, as described:

    #include <memory>
    #include <cassert>
    #include <type_traits>
    #include <typeinfo>
    
    class CloneableInterface {
    public:
        virtual std::unique_ptr<CloneableInterface> Clone() const = 0;
    };
    
    template <class... inherit_from>
    struct InheritFrom : public inherit_from... {
    };
    
    template <class Derived, class AnotherBase = void, bool base_is_cloneable = std::is_base_of_v<CloneableInterface, AnotherBase>>
    class Cloneable;
    
    // three identical implementations, only the inheritance is different
    
    // "no base is defined" case
    template <class Derived>
    class Cloneable<Derived, void, false> : public CloneableInterface {
    public:
        std::unique_ptr<CloneableInterface> Clone() const override {
            assert(typeid(*this) == typeid(Derived));
        return std::make_unique<Derived>(static_cast<const Derived&>(*this));
        }
    };
    
    // Base is defined, and already provides CloneableInterface
    template <class Derived, class AnotherBase>
    class Cloneable<Derived, AnotherBase, true> : public AnotherBase {
       ...
    };
    
    // Base is defined, but has no CloneableInterface
    template <class Derived, class AnotherBase>
    class Cloneable<Derived, AnotherBase, false> : public AnotherBase, public CloneableInterface {
        ...
    };
    

    Usage example:

    class Base : public Cloneable<Base> {
    };
    
    // Just some struct to test multiple inheritance
    struct Other {
    };
    
    struct Derived : Cloneable<Derived, InheritFrom<Base, Other>> {
    };
    
    struct OtherBase {
    };
    
    struct OtherDerived : Cloneable<OtherDerived, InheritFrom<OtherBase>> {
    };
    
    int main() {
        // compiles and runs
        auto base_ptr = std::make_unique<Base>();
        auto derived_ptr = std::make_unique<Derived>();
        auto base_clone = base_ptr->Clone();
        auto derived_clone = derived_ptr->Clone();
    
        auto otherderived_ptr = std::make_unique<OtherDerived>();
        auto otherderived_clone = otherderived_ptr->Clone();
    }
    

    Any critics and improvement suggestions are welcome!