GotW-ish: The ‘clonable’ pattern

Yesterday, I received this question from a distinguished C++ expert who served on the ISO C++ committee for many years. The email poses a decades-old question that still has the classic roll-your-own answer in C++ Core Guidelines #C.130, and basically asks whether we’ve made significant progress toward automating this pattern in modern C++ compared to what we had back in the 1990s and 2000s.

Before I present my own answer, I thought I would share just the question and give all of you readers the opportunity to propose your own candidate answers first — kind of retro GotW-style, except that I didn’t write the question myself. Here is the email, unedited except to fix one typo…


In trying to wrap my mind around all the new stuff for C++20, I see that there is one idea that has been around for quite a while but I still don’t see implemented. I’m wondering whether I’m missing something obvious, or whether it’s still not there.

Suppose B is a base class and I have a shared_ptr<B> that might point to a B or to any class that might be derived from B. Assuming that B and all of its derived classes are CopyConstructible, I would like to create a new shared_ptr<B> bound to a newly created object that is a copy of the object to which my original shared_ptr points. I cannot write this:

shared_ptr<B> b1 = /* whatever */;
shared_ptr<B> b2 = make_shared<B>(*b1);    //NO

because that will bind b2 to a copy of the B part of *b1.

I think I can make this work by putting a member function in B along the following lines:

class B {
    shared_ptr<B> clone() const { return make_shared<B>(*this); }
    // ...
};

and then for each derived class, write something like

class D: public B {
public:
    shared_ptr<B> clone() const {
        return make_shared<D>(*this);   // not make_shared<B>
    }
    // ...
};

and then I can write

shared_ptr<B> b1 = /* as before */;
shared_ptr<B> b2 = b1->clone();

and b2 will now point to a shared_ptr<B> that is bound to an object with the same dynamic type as *b1.

However, this technique requires me to insert a member function into every class derived from B, with ugly bugs resulting from failure to do so.

So my question is whether there some way of accomplishing this automatically that I’ve missed?


So here’s your challenge:

JG Question

1. Describe as many approaches as you can think of that could let us semi- or fully-automate this pattern, over just writing it by hand every time as recommended in C++ Core Guidelines #C.130. What are each approach’s advantages and drawbacks?

Guru Question

2. Show a working Godbolt.org link that shows how class authors can write as close as possible to this code with the minimum possible additional boilerplate code:

class B {
};

class C : public B {
};

class D : public C {
};

and that still permits the class’ users to write exactly the following:

shared_ptr<B> b1 = make_shared<D>();
shared_ptr<B> b2 = b1->clone();

Hints: Feel free to use any/all mechanism that some production or experimental compiler supports (if it’s somewhere on Godbolt, it’s legal to try), including but not limited to:

or any combination thereof.

36 thoughts on “GotW-ish: The ‘clonable’ pattern

  1. I think the motivation to “copy” polymorphic objects is probably out of date, because we could easily add constraints to types with C++20 Concepts, rather than writing a pure virtual base class.

    On the other hand, if there should be multiple implementations for a same interface, I would prefer to add “polymorphic copy” semantics in a “non-intrusive” way introduced in P0957, which is one of my WG21 proposals. According to P0957, we only need to design an addresser that stores an indirect call (like a function pointer) to the copy constructor of the concrete types and it will work. However, because I do not see much motivation in copying objects with polymorphic semantics, and it is unfriendly to non-copyable types (which is also the motivation of “unique_function”), “polymorphic copy” semantics is not supported by the built-in addressers in P0957.

  2. I haven’t tried it yet but can’t the macro versions lose a parameter by using decltype(*this) for the current class?

  3. I believe your approach is incorrect. When calling clone(), a pointer to T is implicitly converted to a pointer to clonable. Because of multiple inheritance and the fact that clonable is the second base of B, this implicit cast may require a fix-up, as the address of the clonable subobject may be different from the address of the complete object B. The fixed-up pointer is first converted to const void* at line 21 and then to const T* at line 16, with this sequence the compiler will not be able to un-fix-up the pointer and thus you trigger UB since p at line 16 may not point to an object of type T.

  4. Here’s a bare-bones, roll-your-own meta-class ‘solution’ https://godbolt.org/z/bWapEF

    using B = metaclass<>;
    using C = metaclass<B>;
    using D = metaclass<C>;
    
    std::shared_ptr<B> b1 = std::make_shared<D>();
    std::shared_ptr<B> b2 = b1->clone();
    

    There’s no boilerplate in the struct definitions (using using)
    but also no actual implementation – the idea would be to inherit interface & implementation.

  5. @Will Wray, my suggestions doesn’t involve constructors. The general idea, that I was lamenting isn’t available, is to define a virtual method in conjunction with a template that will, by default, be used to override the method on ever derived class.

    There is actually already a (kind of) precedent for that with regards to virtual destructors. Every class automatically instantiates that overload, you just don’t get much control over it.

  6. Yes (B Shropshire); we have no virtual constructors / we have no virtual constructors today…

    Here’s the non-virtual equivalent ‘solution’ https://godbolt.org/z/MDFdRv;
    a function pointer in the base is set via an init method templated on the derived type:

    struct B {
        template <typename D>
        void init(D*) { cloner = [](B* b){ return std::make_shared<B>(*static_cast<D*>(b)); }; }
        std::shared_ptr<B> clone() { return cloner(this); }
        std::shared_ptr<B>(*cloner)(B*);
    };
    

    The boilerplate being that init(this) must be called in each derived constructor:

    struct C : public B {  C() { init(this); }};
    
  7. I *so* want the answer to be (the moral equivalent of) this:

    class Base {
     public:
      template<this>  // instantiated in every subclass.
      virtual std::shared_ptr<decltype(*this)> clone() {
        return make_shared<decltype(*this);
      }
    };
    

    … but I suspect that proposals that haven’t even been written up yet are *not* fair game.

  8. I think that I’ve manage to overcome the most unpleasant thing about clone pattern — the possibility to forget to override. I’m sorry, the implementation is a little messy, because it is stripped out directly from production code, with its full generality:

    https://cppx.godbolt.org/z/7khcGT

  9. Quick answer : we don’t need to be intrusive. We can make `shared_ptr` clone-able without adding virtual functions into original classes.
    Note : `std::shared_ptr` already done such polymorphism for the class destructor. We only need to store the copy constructor as well :

    class A {};
    class B : public A {};
    class C : public B {};
    
    void foo()
    {
        auto ptr_b = make_cloneable<B>();
        auto cloned_b = ptr_b.clone();
    }
    

    The key of the implementation is to store type-information on the shared_ptr’s control block:

    struct type_meta {
        std::size_t size;
        std::size_t align;
        void (*destroy)   (void* _this);
        void (*copy_ctor) (void* _this, const void* src);
    };
    

    Full implementation:
    https://gcc.godbolt.org/z/50Wg7j

  10. I hadn’t previously realized how important this problem is.
    I’m scared that I got this far without realizing.

    The fundamental problem (to me) seems to be that we want to (but can’t easily) force every derived type to override a virtual function. Imagine:

    class B {};
    class D : public B {};
    class D2 : public D {};

    Making a pure virtual clone() function in B would force D to override. But it wouldn’t force D2 to also override. As far as I know, there is no way to do this using typical (pure) virtual functionality. https://godbolt.org/z/VqsjGQ
    As I’ve pointed out in that code and in this comment, it is WAY too easy to forget to override. And when you do forget, there doesn’t seem to be any tooling to catch you.

    Interestingly, we can almost do the same thing with CRTP (and get almost the same problems): https://godbolt.org/z/OBFXkp
    This similarly can’t enforce that D2 provided its own method. And now D and OtherD have different base types. So this is worse, but similar.

    To fix the different base types, we could use type erasure: https://godbolt.org/z/zk2Avp
    But so far all I’ve done is get back to the pure virtual example I started with. Only now it is much harder to read.
    We still are able to create a D2 without providing its own method.

    To fix this (and finally do better than pure virtual) we can combine the type erasure with factories to create the derived types and only allow non-factories to pass in their type information: https://godbolt.org/z/Z26W9k

    Taking another glance at that code, I don’t see a way to make it cleaner. It seems to me like it has only the things it needs:
    – A way to enforce every type provides its own clone() information
    – This information is available to the base class
    Seems like a lot of work and difficulty reading for what should have been a simple goal.

  11. I’d like to share an improved version of my previous solution: https://godbolt.org/z/YA9HMq
    It’s now a single class that B inherits from. It’s also possible to use with deep inheritance because it simulates covariant return types (D::clone() also returns std::shared_ptr)

  12. 1. I want to mention that your example (or at least part of the examples I saw in various discussions started from this question) violates ABC (Abstract Base Class) guideline. While it isn’t mentioned in C++ Core guidelines nor in C++ Super-FAQ, it is mentioned in More Effective C++ (Scott Meyers, item 33: Make non-leaf classes abstract). Following this guideline, would remove the main reason behind making the copy c-tor of the concrete (or the abstract base, for that matters) classes private.

    2. I like the pattern of making a public non-virtual clone() that returns the desired smart pointer and protected/private virtual do_clone() that returns a raw pointer. This way, do_clone() can use covariant return type, if desired, and clone() can return the exact smart pointer type relevant even without support for covariant return type on them, as described here: https://www.fluentcpp.com/2017/09/12/how-to-return-a-smart-pointer-and-use-covariance/

    3. Even if the copy c-tor must be private (e.g. to force holding the objects only by shared_ptr), as make_shared needs access to it, I’d better use the “skeleton key” trick (mentioned in GotW in the past, IIRC), by deleting the normal copy c-tor and defining a (public) c-tor that takes an additional parameter of the type of a nested (private/protected) class. This way, only the class can create this parameter so no one else can use this c-tor, but it can pass it to make_shared to use. No need to declare make_shared as friend. This is a problem with so narrow use-case and good enough solutions, so no need to complicate the language nor the library for it.

  13. CRTP solution is probably the easiest, but most(all?) solutions here suffer from easy to forget issues:

    1) the base class needs to have a virtual destructor (due to polymorphic structure every child-class needs to override it, because otherwise shared_ptr will call a wrong destructor).

    2) the base class needs to inherit from std::enable_shared_from_this, because we are about to use the std::shared_ptr in our `clone()` and the control block need to be single for each instance, but `this` is a common leak (use `shared_from_this()` instead of `this`).

    These issues cannot be isolated and implemented once and for all in some kind of “cloneable” CRTP template, but it has to repeated in every base class.

    So I guess the whole point of this exercise is to provide empirical data for meta-classes, right?

  14. Oh yeah, this is indeed 30 a years old problem. I have some slight hope the “metaclasses”-related thing will finally provide a way to put it to rest. My current project goes with macro framework, ENTITY_COMMON(classname, parentname) mandatory insert that adds that clone() function among other things. With an IMPL_ that is needed for the other part of the problem: dynamic instance creation from some type identifier.

    Actually it is a negligible maintenance overhead, as you only need to get the class and parent name right and kept in sync, forgetting is not really possible. The presence of those macros allowed me to evolve the hierarchy-related framework easily and use some interesting tricks for special cases (like processing the code in alternative compilers, statanal tools, ensuring pointer-compatibility, etc),

    And I had something like this in kinda all of my projects in the past (and a few smaller hierarchies beyond the main one in the current too).

    In short I consider macros the go-to way for the practical related cases (can’t recall a single case where clone was the only thing needed/desired), yet look ahead to better ways in upcoming C++.

  15. One problem with a couple of the solutions so far is that `C` is also a base class. I think the real answer is “Why are you using a deep inheritance hierarchy? Don’t do that…”

    Most of the solutions seem to be trending towards abstracting `clone()` from the classes themselves. Which ends up looking like Sean Parent’s “Inheritance is the Base class of Evil” pattern. It would be much more interesting to see how one could automate the implementation of the interface part of that pattern:

    https://godbolt.org/z/BGCND7

  16. When it comes to clone pattern I’ve always been more concerned about unique_ptr and unique_ptr not being covariant than about boilerplate code.

  17. Looks like

    B::clone()

    in the question text was intended to be public virtual.

  18. Here’s a way of doing it by letting Base inherit from Cloneable. All subclasses then inherit from Base::Derive. It also protects you from hard to find mistakes like passing the wrong template parameter or accidentally inheriting from Base instead of Base::Derive.

    https://godbolt.org/z/Cq5T__

  19. Over the course of my career I’ve implemented a few of these sorts of generic cloning systems, but it’s difficult to get away from solutions that don’t require you to add some template customization at each level of inheritence. This can be somewhat more convenient than just doing the cloning pattern from scratch every time, but not really as convenient as you’d like if you’re a C++ nerd.

    Perhaps this is sidestepping the question a bit, but perhaps it would be more elegant to actually implement this within the implementation of `shared_ptr` itself. The question itself already presumes we are using our object with a `shared_ptr`, so I don’t think it’s that unreasonable to consider directly adding cloning support to `shared_ptr` to solve this problem. The control block used by `shared_ptr` already type-erases its deleter, it’s not too far fetched to imagine that it could also virtualize a cloning function as well that type-erases the concrete type that needs to be copied.

    The drawback here is that this would only work if we were guaranteed that the object was created with `make_shared`. That is the only scenario where the creation of the control block happens when we can be certain of the object’s concrete type (since we’re creating it right there!). Conversions from `unique_ptr` or taking ownership or raw pointers cannot make this same guarantee.

    But if we were to instead make a smart pointer type that isn’t `shared_ptr`, but restricts these other conversion scenarios, this could work to provide the functionality that we need.

    Here’s a quick and dirty half-implemented try at a move-only (non-reference counted) pointer that works this way: https://godbolt.org/z/OnYfvd

  20. Seems like metaclasses would let the user code look almost exactly like the target, just replacing class with the to-be-implemented clonable_class. Then just insert the relevant assertion that it’s copy able, and generate code for the clone function. On mobile, so I’ll let someone else get the godbolt points.

  21. For the general case where you have to insert the clone member function into every derived class, I would suggest a small modification to the advice given in C.130.
    Specifically: “For making deep copies of polymorphic classes prefer a **private** virtual clone function instead of copy construction/assignment”.
    Please see the accepted answer in the following StackOverflow question for an example implementation:
    https://stackoverflow.com/questions/43586090/clone-pattern-for-stdshared-ptr-in-c

  22. One possible way would be using a pair of clonable/subclonable classes, one defines the base clone() impl and the other one overrides it: https://godbolt.org/z/ZI0py6 the main drawback is that we have to change the hierarchy of classes (and don’t forget to derive from subclonable).

In comments, use [code] [/code] for code blocks, and the same with angle brackets for code inside text paragraphs. I appreciate and try to acknowledge feedback on GotW articles; if you're suggesting an improvement for a GotW Solution post, please post under your real name (or include it in the comment text) if you would like to be acknowledged here and when the material is updated and expanded in future Exceptional C++ books.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s