Date: Fri, 29 May 2026 05:05:33 +0100
On Thu, 28 May 2026, 16:51 Máté Ték via Std-Proposals, <
std-proposals_at_[hidden]> 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
>
std-proposals_at_[hidden]> 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
>
Received on 2026-05-29 04:05:51
