Date: Tue, 28 Apr 2026 10:51:50 +0200
Hi,
I am late to this discussion, but this issue is very fascinating.
Disclaimer: I can't type this mail, I am on my way to work, so I'll use
ChatGPT voice-to-text to phrase my thoughts, so if it feels AI-generated,
it's because it is, but they are very much my thoughts. Also keep in mind
that I am no authority here, I am just a regular C++ enthusiast, and this
is just my opinion.
I think to understand the deeper issue here, we have to transcend C++ for a
bit and be a bit philosophical about classes.
A class is essentially a collection of data members and member functions.
Each member—both functions and variables—has a visibility: public,
protected, or private.
The issue is that we need multiple different renditions of this abstract
class. In a way, just like a person presents different aspects of
themselves depending on context, a class also has different “faces.” It is
still the same entity, but we expose different parts of it depending on who
is interacting with it.
In software, this means the same class must have a public exposition for
its users. They need to see the public (and possibly protected) members so
they can use them. They also need to see all data members—including private
ones—because those contribute to the physical memory layout of the class.
This is already a point of friction: private members are not accessible,
yet they must still be visible for layout reasons. That’s simply a
consequence of how physical computers work, and we must accept that.
But we should ask: is this also true for private member functions? It seems
the answer is no. They do not seem to affect layout, so they don’t strictly
need to be declared in the header. In that sense, the proposal is
well-founded.
If we continue this idea of multiple renditions, we arrive at the need for:
- a public exposition of the class, and
- a rendition for the implementation.
A naïve approach—having two completely separate interface
declarations—would require duplicating all public members and data members
in two places. That is clearly unacceptable, because any change to the
public interface would need to be made twice.
A more viable approach is to let the implementation-side declaration
specify only the delta—i.e., what is added on top of the public interface.
Now, one open question I have is about private virtual functions. Even
though they don’t affect the object layout directly, they do contribute
entries to the vtable. I’m not sure whether they can be omitted from the
exposition without issues. I’d appreciate input from someone with deeper
expertise in this area.
>From a syntax perspective, we should also be careful. This feature would
need to coexist with existing code, and it could be confusing if a reader
suddenly sees private member functions “missing” from the class definition
and moved elsewhere.
For that reason, I think a clear, opt-in mechanism would be important—for
both humans and tooling.
One possible direction would be to introduce new standard attributes. For
example:
// header
class [[exposition]] MyClass {
public:
void foo();
private:
int data;
};
// .cpp
class [[implementation]] MyClass {
private:
void helper(); // only visible here
};
This makes the intent explicit:
the header provides the exposition,
the source file provides the implementation extension.
It would be immediately clear to readers that the class opts into this
model, and IDEs could also take advantage of this structure.
>From a tooling and language perspective, this would require compiler and
linker support. The rule could be that all translation units see the same
public exposition of the class, just like today. However, if this opt-in
feature is used, there must be exactly one implementation definition of the
class. If it is missing, that would be a link-time error; if multiple
implementations exist, that would also be a linker error. This is
completely analogous to how ordinary functions behave, so it seems
realistically supportable.
This model should also work naturally with modules: only the [[exposition]]
interface would be exported, while the [[implementation]] part can remain
internal to the module.
IMO this sould be a purely optional, opt-in feature—more of an extension
than a 'breaking' language change.
Sincerely,
Matthew
Steve Weinrich via Std-Proposals <std-proposals_at_[hidden]> ezt írta
(időpont: 2026. ápr. 28., Ke 6:50):
> Hi André,
>
> IMHO, the only way to achieve what you are looking for (in something of a
> secure manner) is to invent a new rule for the compiler. Something like,
> “One of the constructors for a class may be marked with a new attribute
> [[KEY]]. All methods marked with the [[FRIEND class-name]] attribute
> within the same compilation unit as the class constructor containing the
> [[KEY]] attribute will be granted friend access to the specified class.”
> Note that I am using attributes here, but they could not be used like this
> as the code must be able to be compiled without support for particular
> attributes. This is just presenting a notion.
>
> I hope that helps.
>
> Chers,
> Steve
>
>
>
> *From:* Std-Proposals <std-proposals-bounces_at_[hidden]> *On Behalf
> Of *André Offringa via Std-Proposals
> *Sent:* Monday, April 27, 2026 1:21 AM
> *To:* std-proposals_at_[hidden]
> *Cc:* André Offringa <offringa_at_[hidden]>
> *Subject:* Re: [std-proposals] Translation-unit-local functions that
> access private class fields
>
>
>
> Hi Steve,
>
> Thanks for the feedback. You're suggesting helper friend classes as an
> alternative, but there are some issues with a helper friend class to enable
> translation-unit-local functions. First of all, to make the functions
> translation-unit-local, it must be placed in an anonymous namespace. To do
> this, it must be done so before the befriending, so something like this:
>
> == header file ==
>
> namespace {
> struct FooFriend;
> }
>
> class Foo {
> int a;
> friend FooFriend;
> };
>
> == cpp file ==
>
> namespace {
> struct FooFriend {
> static void SetA(Foo& foo) {
> foo.a = 3;
> }
> };
> }
>
> I think this is an awkward construct. The Google C++ style explicitly
> forbids this, as it doesn't allow unnamed namespaces in header files. The
> SetA() function is also now publicly available, and even though that is
> limited to a single translation unit, it is still easy to accidentally
> break a class invariant in this way. The classname of FooFriend is leaked
> out of the scope of the unit file (style guides often mandate to put it in
> yet another subnamespace like 'details' in that case). Finally, the syntax
> of the call and use of private variables is more verbose. Compare it to:
>
> == header file ==
>
> class Foo {
> int a;
> };
>
> == cpp file ==
>
> private Foo::SetA() {
> a = 3;
> }
>
>
>
> So, while there are alternatives for private functions, I don't think the
> friend classes are really a solution for the issue that the proposal tries
> to solve.
>
> Kind regards,
> André
>
> On 4/26/26 11:05 PM, Steve Weinrich via Std-Proposals wrote:
>
> This is purely subjective, but I don't find that a single line, "friend
> class Helper;" is clutter. It can be put at the very bottom of the class.
> It gives Helper full access for whatever purpose. No committee required!
>
>
>
> Cheers,
>
> Steve
>
>
>
> On Sun, Apr 26, 2026, 00:17 André Offringa via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
> On 4/25/26 10:56 PM, Steve Weinrich via Std-Proposals 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.
>
>
>
> What I'm trying to solve:
>
> - Wen using friend declarations, these clutters the class with
> implementation details that do not affect the layout of the class.
>
> - For FooHelper(), it would require the return and parameters types to be
> available at the place of the friend declaration, creating extra
> dependencies. Your example takes only Foo*, but I think it's common for a
> helper function to use other parameters (and/or have a return value), which
> causes the dependencies.
>
> Regards,
> André
>
>
>
>
>
> 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
>
>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
I am late to this discussion, but this issue is very fascinating.
Disclaimer: I can't type this mail, I am on my way to work, so I'll use
ChatGPT voice-to-text to phrase my thoughts, so if it feels AI-generated,
it's because it is, but they are very much my thoughts. Also keep in mind
that I am no authority here, I am just a regular C++ enthusiast, and this
is just my opinion.
I think to understand the deeper issue here, we have to transcend C++ for a
bit and be a bit philosophical about classes.
A class is essentially a collection of data members and member functions.
Each member—both functions and variables—has a visibility: public,
protected, or private.
The issue is that we need multiple different renditions of this abstract
class. In a way, just like a person presents different aspects of
themselves depending on context, a class also has different “faces.” It is
still the same entity, but we expose different parts of it depending on who
is interacting with it.
In software, this means the same class must have a public exposition for
its users. They need to see the public (and possibly protected) members so
they can use them. They also need to see all data members—including private
ones—because those contribute to the physical memory layout of the class.
This is already a point of friction: private members are not accessible,
yet they must still be visible for layout reasons. That’s simply a
consequence of how physical computers work, and we must accept that.
But we should ask: is this also true for private member functions? It seems
the answer is no. They do not seem to affect layout, so they don’t strictly
need to be declared in the header. In that sense, the proposal is
well-founded.
If we continue this idea of multiple renditions, we arrive at the need for:
- a public exposition of the class, and
- a rendition for the implementation.
A naïve approach—having two completely separate interface
declarations—would require duplicating all public members and data members
in two places. That is clearly unacceptable, because any change to the
public interface would need to be made twice.
A more viable approach is to let the implementation-side declaration
specify only the delta—i.e., what is added on top of the public interface.
Now, one open question I have is about private virtual functions. Even
though they don’t affect the object layout directly, they do contribute
entries to the vtable. I’m not sure whether they can be omitted from the
exposition without issues. I’d appreciate input from someone with deeper
expertise in this area.
>From a syntax perspective, we should also be careful. This feature would
need to coexist with existing code, and it could be confusing if a reader
suddenly sees private member functions “missing” from the class definition
and moved elsewhere.
For that reason, I think a clear, opt-in mechanism would be important—for
both humans and tooling.
One possible direction would be to introduce new standard attributes. For
example:
// header
class [[exposition]] MyClass {
public:
void foo();
private:
int data;
};
// .cpp
class [[implementation]] MyClass {
private:
void helper(); // only visible here
};
This makes the intent explicit:
the header provides the exposition,
the source file provides the implementation extension.
It would be immediately clear to readers that the class opts into this
model, and IDEs could also take advantage of this structure.
>From a tooling and language perspective, this would require compiler and
linker support. The rule could be that all translation units see the same
public exposition of the class, just like today. However, if this opt-in
feature is used, there must be exactly one implementation definition of the
class. If it is missing, that would be a link-time error; if multiple
implementations exist, that would also be a linker error. This is
completely analogous to how ordinary functions behave, so it seems
realistically supportable.
This model should also work naturally with modules: only the [[exposition]]
interface would be exported, while the [[implementation]] part can remain
internal to the module.
IMO this sould be a purely optional, opt-in feature—more of an extension
than a 'breaking' language change.
Sincerely,
Matthew
Steve Weinrich via Std-Proposals <std-proposals_at_[hidden]> ezt írta
(időpont: 2026. ápr. 28., Ke 6:50):
> Hi André,
>
> IMHO, the only way to achieve what you are looking for (in something of a
> secure manner) is to invent a new rule for the compiler. Something like,
> “One of the constructors for a class may be marked with a new attribute
> [[KEY]]. All methods marked with the [[FRIEND class-name]] attribute
> within the same compilation unit as the class constructor containing the
> [[KEY]] attribute will be granted friend access to the specified class.”
> Note that I am using attributes here, but they could not be used like this
> as the code must be able to be compiled without support for particular
> attributes. This is just presenting a notion.
>
> I hope that helps.
>
> Chers,
> Steve
>
>
>
> *From:* Std-Proposals <std-proposals-bounces_at_[hidden]> *On Behalf
> Of *André Offringa via Std-Proposals
> *Sent:* Monday, April 27, 2026 1:21 AM
> *To:* std-proposals_at_[hidden]
> *Cc:* André Offringa <offringa_at_[hidden]>
> *Subject:* Re: [std-proposals] Translation-unit-local functions that
> access private class fields
>
>
>
> Hi Steve,
>
> Thanks for the feedback. You're suggesting helper friend classes as an
> alternative, but there are some issues with a helper friend class to enable
> translation-unit-local functions. First of all, to make the functions
> translation-unit-local, it must be placed in an anonymous namespace. To do
> this, it must be done so before the befriending, so something like this:
>
> == header file ==
>
> namespace {
> struct FooFriend;
> }
>
> class Foo {
> int a;
> friend FooFriend;
> };
>
> == cpp file ==
>
> namespace {
> struct FooFriend {
> static void SetA(Foo& foo) {
> foo.a = 3;
> }
> };
> }
>
> I think this is an awkward construct. The Google C++ style explicitly
> forbids this, as it doesn't allow unnamed namespaces in header files. The
> SetA() function is also now publicly available, and even though that is
> limited to a single translation unit, it is still easy to accidentally
> break a class invariant in this way. The classname of FooFriend is leaked
> out of the scope of the unit file (style guides often mandate to put it in
> yet another subnamespace like 'details' in that case). Finally, the syntax
> of the call and use of private variables is more verbose. Compare it to:
>
> == header file ==
>
> class Foo {
> int a;
> };
>
> == cpp file ==
>
> private Foo::SetA() {
> a = 3;
> }
>
>
>
> So, while there are alternatives for private functions, I don't think the
> friend classes are really a solution for the issue that the proposal tries
> to solve.
>
> Kind regards,
> André
>
> On 4/26/26 11:05 PM, Steve Weinrich via Std-Proposals wrote:
>
> This is purely subjective, but I don't find that a single line, "friend
> class Helper;" is clutter. It can be put at the very bottom of the class.
> It gives Helper full access for whatever purpose. No committee required!
>
>
>
> Cheers,
>
> Steve
>
>
>
> On Sun, Apr 26, 2026, 00:17 André Offringa via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
> On 4/25/26 10:56 PM, Steve Weinrich via Std-Proposals 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.
>
>
>
> What I'm trying to solve:
>
> - Wen using friend declarations, these clutters the class with
> implementation details that do not affect the layout of the class.
>
> - For FooHelper(), it would require the return and parameters types to be
> available at the place of the friend declaration, creating extra
> dependencies. Your example takes only Foo*, but I think it's common for a
> helper function to use other parameters (and/or have a return value), which
> causes the dependencies.
>
> Regards,
> André
>
>
>
>
>
> 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
>
>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2026-04-28 08:52:05
