On Fri, Jun 21, 2019 at 11:32 AM Matthew Woehlke <mwoehlke.floss@gmail.com> wrote:
On 20/06/2019 18.59, Arthur O'Dwyer via Std-Proposals wrote:
> If you're allowed to rewrite the implementation of `create`, then [...]
> https://godbolt.org/z/msXcjJ
>
>     static std::shared_ptr<a> create(int x) {
>         struct Wrapper {
>             int x;
>             operator a() const { return a(x); }
>         };
>         return std::make_shared<a>(Wrapper{x});
>     }

There's enough assembly being generated that it's hard to be certain,
but offhand, it seems that this generates worse code? (It certainly
generates *more*: https://godbolt.org/z/lGaz4j.)

No, the codegen is identical.
https://godbolt.org/z/tVI70Q
Remember that when comparing codegen, you have to use the same optimization level and the same compiler. On Godbolt, you also have to make sure that the same checkboxes are ticked in both panes.

 
> Regardless, I don't see how your idea about "friend scope" would help at
> all. Your whole problem is that the access-to-protected-constructor happens
> in a *non-friend* scope, buried deep inside `make_shared`.  Adding a
> "friend scope" somewhere inside A::create won't help a scope inside
> `make_shared` access protected members.

...then you misunderstood the idea. The scope applies to *all code*,
including recursively called code, within the scope. (Well, sort of;

Well, not!  You're trying to use the word "scope" to mean something other than "scope".  In C++, "scope" means, like, a lexical scope: the scope between a left-brace and a right-brace. What you're describing is a way to dynamically turn off access control, so that for example `foo()` compiles when called from `baz()`, but doesn't compile when called from `bar()`.

class B { int m; };
B b;
void foo() { b.m = 42; }
void bar() { foo(); }
void baz() { friend { foo(); } }

That's not something that can ever work, with separate compilation. We can't have two different versions of the code for `foo` floating around out there.  (And yes, you can SFINAE on the ability to access a member.)

[...]
The reason I think this *may* be plausible is that compilers generate a
call stack when an access protection violation occurs. For example:

  in <implementation details>
  required by <more implementation details>
  ...
  required by `std::make_shared<a>(T...)` (with `T...` = `{int}`)
  error: `a::a(int)` is protected

That's not a call stack; there is no implication that std::make_shared<a> calls <more implementation details>.
What it is is an instantiation stack. Instantiating make_shared<a> requires the compiler to instantiate <more implementation details>. Maybe it's getting instantiated so that we can call it at runtime; maybe it's getting instantiated so we can take its address; maybe it's getting instantiated so we can take its decltype() and for no other reason.

Anyway, given that the codegen for `Wrapper<a>` is identical, is this all a moot point now?

HTH,
Arthur