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…









[image error]



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 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 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 b1 = /* whatever */;
shared_ptr b2 = make_shared(*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 clone() const { return make_shared(*this); }
// ...
};

and then for each derived class, write something like


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

and then I can write


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

and b2 will now point to a shared_ptr 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?






[image error]







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 b1 = make_shared();
shared_ptr 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:





the very old (e.g., macros),C++11/14/17 features (e.g., decltype),draft C++20 features (e.g., concepts),proposals currently before the committee for future C++ (e.g., detection idiom),



or any combination thereof.

1 like ·   •  0 comments  •  flag
Share on Twitter
Published on September 24, 2019 18:01
No comments have been added yet.


Herb Sutter's Blog

Herb Sutter
Herb Sutter isn't a Goodreads Author (yet), but they do have a blog, so here are some recent posts imported from their feed.
Follow Herb Sutter's blog with rss.