On Thu, 28 May 2026, 16:51 Máté Ték via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:

The real issue with classic PEMs IMO is that they live in the same "name space" as the "first-class class members".
This is what generates the friction with overload resolution/ODR.
So I thought, let's give them a "name space" within the class they're extending!
After some fooling around with various syntaxes, here's my strongest candidate:

/// MyClass.hpp
class MyClass {
public:
    void Foo(int);
    void Bar(int);
private:
    int mySecret;
};

/// MyClass.cpp
namespace {
    private extension MyClass::impl {
        void FooBar(int i) { mySecret += i; }
        // virtual void Gaz();  ERROR: extension member function cannot be virtual
        // int otherSecret;     ERROR: extension cannot have non-static data members
        static int otherSecret; // OK, why not?
    };
    // I could have defined FooBar out-of-line like so:
    void MyClass::impl::FooBar(int i) { mySecret += i; }
}
void MyClass::Foo(int i) { impl::FooBar(i + 1); }
void MyClass::Bar(int i) { impl::FooBar(i * 2); }
// static void SomeFunction(MyClass& x) { x.impl::FooBar(42); }  ERROR: class interface extension MyClass::impl is private


Thus we eliminated the overload resolution/ODR concerns.
It is now very clear that PEMs are not "first-class" members of the class.
The interface of the class, and therefore its "identity" is undisputed and unfractured.

A similar thing can be implemented with existing language features:

// header
class X {
private:
  // ...
  struct impl;
  friend impl;
public:
  int public_interface();
};

// source
struct X::impl {
  static int helper(X& self) {
    // ...
  }
};
int X::public_interface() {
  return impl::helper(*this);
}


This loses the easier syntax of an implicit object argument and has external linkage (though you can work around that with `namespace detail { namespace { struct X_helper; } } ` and `friend detail::X_helper;`), but is implementable today without exposing too much in the header.

Encapsulation is not broken.
Interface extensions naturally follow the good ol' class syntax.

Encapsulation/access control does seem broken. This could allow private member access in any translation unit by just defining an extension. This could be fixed by requiring a declaration of the extension but that is closer to something like Java interfaces as opposed to Rust impl/traits