C++ Logo

sg20

Advanced search

Re: [SG20] [isocpp-lib-ext] Can't befriend make_unique/make_shared

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Wed, 25 Sep 2019 14:41:45 -0400
Oh, and one more thing (besides trying this alternative address for SG20's
list)—

If such an arcane mechanism were added, and anyone actually used it, I
predict that it would be about three months before we saw the first
StackOverflow question asking:

"I'm trying to use `class Widget`, which befriends `std::make_shared`. But
I actually want to { create `optional<Widget>`, use `Widget` with
`boost::shared_ptr`, derive from `Widget` }, and now I'm having difficulty
accessing Widget's constructor. How do I get access to the constructor of a
`Widget` that tries to lock down access in this way?"

...and then the arms race will start up again in the StackOverflow answers.

–Arthur



On Wed, Sep 25, 2019 at 2:36 PM Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
wrote:

> On Wed, Sep 25, 2019 at 10:56 AM Tony V E via Lib-Ext <
> lib-ext_at_[hidden]> wrote:
> >
> > On Wed, Sep 25, 2019 at 1:23 AM Matt Calabrese via Lib-Ext <
> lib-ext_at_[hidden]> wrote:
> >>
> >> I agree that this is a problem and does come up sometimes in practice,
> though I do not believe that creating a solution specifically for
> make_shared or make_unique is the best option. IMO, this is a small symptom
> of the overall limited approach to how we do emplace and make operations in
> C++.
> >> [...]
> >
> > You could go one step further, and look up the lambda (or "factory") via
> a trait. So make_unique<Foo>(a,b,c) would call
> std::construction_trait<Foo>::factory(a,b,c) (which can be a friend of Foo).
> > And by default, construction_trait<T>::factory(...) just forwards to the
> constructor (so current stuff still works), or (equivalently modulo sfinae)
> make_unique uses if constexpr to check for existence of the factory, calls
> it, else calls the constructor.
>
>
> Please, please — do not introduce complexity where no complexity currently
> exists or is needed!
> C++ has a very deserved reputation as an "experts-only language." It is
> hard to teach. It is hard to use. It is even hard to know what the rules
> for using it are (because there are so many arcane little rules).
>
> In Herb's case, he's got a student coming to him with a question:
>
> class C {
> C(int x);
> };
> auto p = std::make_shared<C>(42);
>
> Student: "Why doesn't this compile?"
> Teacher: "Ah, it's because `make_shared` can't access your constructor.
> Make your constructor public."
> Student goes off for a while and comes back, having failed to follow the
> teacher's advice.
>
> class C {
> C(int x);
> template<class T, class... Ts> friend std::shared_ptr<T>
> std::make_shared(Ts&&...);
> };
> auto p = std::make_shared<C>(42);
>
> Student: "This still doesn't work!"
> Teacher: "You are making the code too complicated. If you want some other
> library to have access to your constructor, make your constructor public."
> Student: "Why doesn't it work, what I wrote?"
> Teacher: "The constructor call might be buried several layers of
> indirection deep, in a helper function."
> Teacher: "Besides, even if it weren't, I'm honestly not sure that you got
> the declaration of `make_shared` correct. The Committee actually has a set
> of guidelines somewhere saying not to forward-declare functions or
> templates from `std`, because you can't possibly get them right. What you
> wrote might work on one implementation and break quietly on another."
> Teacher: "Besides, `friend` is a code smell."
>
> If WG21 added a mechanism for code like this to "befriend make_shared" — a
> necessarily arcane and therefore poorly understood mechanism — well, the
> student would come back and say something like "Ah, I've found this snippet
> on StackOverflow. Can you explain to me how it works? Can I use this in my
> own code?" and we'll have to go through one or two more rounds of this
> dialogue.
>
> The teacher's answer *still won't have changed*. You shouldn't do this.
> `friend` is a code smell. If you want a third party to be able to access
> your constructors and other methods, make them public. The `private`
> keyword is specifically for *preventing* access from third parties such
> as make_shared.
>
> The fact that your code didn't work on the first try is a teaching
> opportunity; it gives me the chance to explain about public and private,
> and helper methods, and the rule about not trying to forward-declare (or
> inherit from, or befriend) facilities out of `namespace std`.
>
> Complexifying the situation will not help the student, nor the teacher,
> nor the working programmer (who, having been a student once, instantly
> knows that the solution is to mark the constructor `public` and move on
> with his life). Complexifying the situation will *harm* the former two
> and likely be ignored by the latter.
>
> –Arthur
>

Received on 2019-09-25 13:44:07