On Thu, Jun 20, 2019 at 5:41 PM Matthew Woehlke via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
I am struggling with the following code (grotesquely simplified):

  class a
  {
  public:
    static std::shared_ptr<a> create(int x)
    { return std::make_shared<a>(x); }

  protected:
    friend class std::shared_ptr<a>;

    a(int) {}
  };

...and I'm hardly alone: https://stackoverflow.com/questions/8147027.

Most of the existing approached are either ugly (requiring introduction
of "public" API that is made unusable outside of the class by using a
private type in the parameter list), or essentially relies on various
methods to circumvent access protection and can only access protected
constructors.

It occurred to me, there really ought to be a better way to fix this.
(Also, smart pointer return type polymorphism, but that's a different
discussion.) Poking around in libstdc++'s implementation of shared_ptr
inspired me to try friending `std::shared_ptr<a>`, but that doesn't work
either, since it is ultimately a helper function that constructs `a`. I
then wondered if it would be completely unreasonable to specify an
implementation detail that would permit this, although that's really
still the wrong thing to do.

Some libraries, such as boost::iterator_facade, do take that approach.
The standard library could take that approach without an ABI break, AFAIK; all it would need is someone to make the proposal.
 
Then I had a better, though perhaps crazy idea... what about introducing
a "friend scope"?

  static std::shared_ptr<a> create(int x)
  {
    friend
    {
      // Within this block, all access protection to `a` is suspended.
      ...
    }
  }

If you're allowed to rewrite the implementation of `create`, then you could just pull the construction up to `create`'s level:

    static std::shared_ptr<a> create(int x) {
        return std::shared_ptr<a>(new a(x));
    }

Or if you must use `make_shared` for its memory-footprint advantages, you can do it via some insanity like:

    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});
    }

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.

HTH,
–Arthur