40

GotW-ish: The ‘clonable’ pattern

 4 years ago
source link: https://www.tuicool.com/articles/zMFF3yQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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…

fYZr6j7.png!web

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?

En6NNnB.png!web

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 Goldbolt.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:

  • 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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK