C++ Logo

std-proposals

Advanced search

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

From: André Offringa <offringa_at_[hidden]>
Date: Sun, 26 Apr 2026 08:44:12 +0200
On 4/26/26 8:12 AM, Simon Schröder via Std-Proposals wrote:

> There is no ‘unit file’ in C++. This means that any file could just
> introduce their own “helper” function (“helper” meaning that we want
> to access something from the outside we are not supposed to access).
> However, C++ always requires the header file anyway and this can
> obviously be edited. Everyone can just change ‘private’ to ‘public’
> (though there are some theoretical limitations because of ordering
> guarantees which would not work with existing compiled libraries
> according to the standard) or introduce a friend function. We just
> usually don’t do this with the STL or some other external library.
> However, with your proposed solution we could easily extend any class
> or access private members from any file. Especially when updating
> external libraries this modification would now persist whereas
> changing the header file needs to be repeated. We should not make it
> this easy.

I'm not sure I follow. While the function could indeed be added to any
file and access private members, the function itself would only be
callable from other methods/functions that have access to private
fields. If it's added to a file that doesn't have methods/functions with
access to the private fields, it will be uncallable from there and it
would act as an unused static free function.

I don't see this as extending the class; the class layout and definition
remains the same. It's akin to adding a static free function to a
translation unit.

Regards,

André


>
> However, this brought me to a different idea: In theory we got modules
> in C++. Maybe it would make sense to allow helper functions inside the
> module (as long as they are not exported). So, we might have
> visibility ’module’ for member variables. I guess, we still need to
> differentiate between ‘private’ and ‘protected’ visibility. This would
> require ‘module private’ or ‘module protected’. This would reuse an
> existing keyword. I suspect that this ordering of the keywords is
> harder to parse and we might just want the order ‘private module’ and
> ‘protected module’. The meaning of this would be that every
> freestanding function inside the same module as the class which is not
> exported by the module can access these members.
>
> I just came up with yet another idea that can help with hiding the
> private implementation (at least a little bit):
>
> class Foo_helper
> {
> protected:
> void helper(auto this self);
> }
>
> class Foo : Foo_helper
> {
> …
> }
>
> This is using ‘deducing this’ inside a helper class. Though, now that
> I’m thinking about this, I’m not entirely sure if we would need to
> make Foo_helper a friend of Foo to allow it to access private and
> protected members of Foo. Other than that, notice that private
> inheritance also makes the members of Foo_helper private inside Foo.
> Additionally, with the use of #ifdefs we might be able to provide an
> empty class declaration for Foo_helper inside the header file and a
> different one inside the unit file (including the header after
> defining Foo_helper). This could truly hide the private interface.
> But, it is only useful if we don’t need to make Foo_helper a friend of
> Foo.
>
> One problem you might not have thought about is that just declaring
> private void Foo::helper() inside the unit file without having it
> inside the class declaration is that you need to properly order your
> helper functions so that they can call each other. Or we introduce
> that we can first declare them in the unit file (most likely at the
> top right after the include of the header) and then later define them.
>
>> On Apr 25, 2026, at 10:57 PM, Steve Weinrich via Std-Proposals
>> <std-proposals_at_[hidden]> wrote:
>>
>> 
>> I may have missed something, but one can declare:
>>
>> class Foo
>> {
>> friend class Helper;
>>
>> friend void FooHelper (Foo *);
>> };
>>
>> Both the class Helper and the function FooHelper() can access all
>> private members of Foo. Thus, only the names and/or the function
>> signature is declared in the interface.
>>
>> On Sat, Apr 25, 2026, 14:46 André Offringa via Std-Proposals
>> <std-proposals_at_[hidden]> wrote:
>>
>> Hi all,
>>
>> I was wondering what people think of the following idea. The
>> problem I'm
>> trying to address is that if we want to introduce a helper method
>> for a
>> class, we have to declare this helper function in the class, e.g.
>> assume
>> this situation:
>>
>> == Header file: ==
>>
>> class Foo {
>> public:
>> void A();
>>
>> private:
>> void Helper();
>>
>> int value_;
>> };
>>
>> == Unit file: ==
>>
>> void Foo::A() {
>> ...
>> Helper();
>> ...
>> }
>>
>> void Foo::Helper() {
>> ...
>> value_ = ...;
>> ...
>> }
>>
>> I think it would be useful if there would be a way to skip the
>> declaration of the Helper method inside the class (in the header
>> file),
>> and make it translation local just like a static function or
>> function
>> inside an anonymous namespace would be. From the compiler's point of
>> view, it could then act as a translation-unit-local function, except
>> with the possibility to access (private) class fields.
>>
>> The benefit is that the method is no longer part of the
>> "interface" of
>> the class, and this is useful because it is, after all, an
>> implementation detail of the class. This makes it also no longer
>> necessary to have the parameter types and return value type
>> declared in
>> the header file, which decreases dependencies between files.
>>
>> An example of how this could look like, could be to use the keyword
>> 'private' and let it act as an identifier for declaring such a
>> function,
>> e.g.:
>>
>> == Header file: ==
>>
>> class Foo {
>> public:
>> void A();
>>
>> private:
>> int value_;
>> }
>>
>> == Unit file: ==
>>
>> private void Foo::Helper() {
>> ...
>> value_ = ...;
>> ...
>> }
>>
>> void Foo::A() {
>> ...
>> Helper();
>> ...
>> }
>>
>> Of course the syntax is open for discussion. The idea is that
>> Helper()
>> is now a private translation-unit-local function that receives the
>> 'this' pointer and access to private fields. The function itself
>> acts in
>> name lookup as a free function, to avoid participating in member
>> lookup,
>> but is only visible inside class member functions or other private
>> translation-unit-local functions, and is not accessible outside
>> of that.
>> This makes it somewhat between a member function and a free
>> function.
>> With such an approach, it can not be used to access private
>> fields from
>> a scope that does not allow access to those fields. Hence, the class
>> data remains encapsulated. It should not modify the layout of the
>> class
>> and not change its ABI. There are more details to think through.
>>
>> Thinking of alternatives, another direction to solve this would
>> be to
>> change the standard such that friend functions can be declared as
>> friend
>> outside of the class definition, instead of by introducing a
>> function
>> with special visibility rules. They would then behave as normal
>> functions, which simplify some details. This makes private data too
>> widely usable, so I don't see a good solution in that direction.
>>
>> Syntax aside, the problem I'm trying to solve is to have a
>> function that:
>> - has access to private members
>> - is defined only in the unit file
>> - does not require any declaration in the header
>> - does not become part of the class interface
>>
>> I think the best existing alternative for this situation is to
>> declare a
>> static free function in the unit file that takes as parameter the
>> class
>> members it needs. In complex situations, this is not as nice. In
>> pimpl
>> implementations it is a reasonable solution, but a pimpl pattern
>> is not
>> always desired.
>>
>> I'm curious to hear what people think about the idea of private
>> translation-unit-local functions.
>>
>> Kind regards,
>> André Offringa
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2026-04-26 06:44:19