C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Translation-unit-local functions that access private class fields

From: Máté Ték <eppenpontaz_at_[hidden]>
Date: Thu, 28 May 2026 17:51:21 +0200
Thanks for the feedback.

I made some real progress with the theory, I think.

> I would also argue that this is not a build-system optimization.
I did not fully understand my own thoughts either. I am truly sorry for
that, it's just how my brain works.
I would put it this way:
The originally proposed feature, which is maximally stripped down and quite
primitive, is IMO not substantial enough to be a full-blown language
feature.
The reason is, it gives us (relatively) few benefits while generating a lot
of friction (with existing language rules).
I know that the goal is to discover a new language feature that would allow
us to physically hide the implementation details of classes, and is more
than a build-system optimization.
What I really meant to say is that I don't think we're there yet.


I already described an "equivalence principle" in my previous emails, which
I dub the "inline function equivalence" (pretty sure I am not the first to
come up with it though).
My conclusion was that PEMs must be invoked with a special syntax, and must
not be found by "regular" name lookup / overload resolution, or else we
would end up with an "unsafe" language feature.
I did not know how valuable this insight is, until I found a precedent:
https://en.cppreference.com/cpp/language/access#In_detail
> Member access does not affect visibility: names of private and
privately-inherited members are visible and considered by overload
resolution, implicit conversions to inaccessible base classes are still
considered, etc.
> Member access check is the last step after any given language construct
is interpreted.
> The intent of this rule is that replacing any `private` with `public`
never alters the behavior of the program."
This is very much an equivalence principle to me.
(Silly, I should have found this a while ago. I didn't do my homework, it
seems.)
Therefore I think that any rendition of this proposal that fails to address
the "inline function equivalence principle" could very well be vetoed by
the standard committee.

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.
Encapsulation is not broken.
Interface extensions naturally follow the good ol' class syntax.
They can be naturally placed into an unnamed namespace to get internal
linkage if we want that, but it's not mandatory.
But we could also have public or protected interface extensions too:

/// SomeOtherFile.cpp
void UseMyClass(std::vector<MyClass>& xs) {
    // Function-local interface extension, why not?
    public extension MyClass::ext {
        void Gaz() { Bar(0); } // OK, MyClass::Bar(int) is public
        // int GetSecret() const { return mySecret; } ERROR: public
interface extension cannot access private member MyClass::mySecret
    };
    std::ranges::for_each(xs, MyClass::ext::Gaz); // OK, MyClass::ext is a
public interface extension, usable outside the class too
}

Thoughts?

Sincerely,
Matthew

On Thu, 14 May 2026 at 15:00, Rhidian De Wit via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I would also argue that this is not a build-system optimization. It
> *could* lead to better build-times, but that's just a side benefit we get
> along, not the core issue we're trying to tackle.
>
> For me the core issue is the fact that header files can easily be polluted
> with unnecessary things (which *can* worsen build times, but again, not
> the main concern IMO). It's the fact that when defining public API's or
> libraries we don't need to expose internals.
> I also want to explicitly support header files and not only modules,
> because plenty of large codebases do not work on modules and could benefit
> from PEMs.
>
> I don't advocate for re-opening the class-scope, I think that would
> present more problems than we would get solutions. I'd love the PEMs to be
> a simple feature with simple rules:
>
> A PEM can only be accessible to 1 TU (meaning that they should always have
> internal linkage) and is able to access the private members of the class it
> extends.
> It is not allowed to overload with member functions of the class it
> extends.
>
> Perhaps, if this goes well, it could in the future be expanded to just
> re-opening the class scope? But I don't think this is the correct time to
> do such a thing however
>
> Op do 14 mei 2026 om 09:21 schreef Simon Schröder via Std-Proposals <
> std-proposals_at_[hidden]>:
>
>>
>>
>> > On May 14, 2026, at 12:12 AM, Máté Ték via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>> >
>> > I did some more thinking and I think this proposal is really a build
>> system optimization disguised as a language feature.
>>
>> You are right that this could improve build times. However, for the main
>> point would rather be that the header file (which is read by users of my
>> class) is not trashed with private methods that no user of my class needs
>> to know about. If there were a way to just have public methods (and public
>> member variables) inside the header file I would even advocate for that.
>> But so far, the compiler needs all member variables to know the class
>> layout.
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
>
> --
> Rhidian De Wit
> Software Engineer - Barco
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>


-- 
Üdv.: Máté

Received on 2026-05-28 15:51:36