Date: Wed, 29 Apr 2026 11:51:20 +0200
I'd like to ask for some clarification on these thoughts:
> C++ has 2 important boundaries
> - translation unit: internal vs. external linkage
> and
> - classes
> - together with namespaces (a class cannot be in two sibling namespaces
at the same time)
> [Orthogonal concepts:
> - block scopes have to be inside both
> - With the preprocessor one TU can have several headers, but one header
can also be in several TUs
> - similar modules]
> With templates and inline code TUs are a bad protection for encapsulation.
> So creating public member functions outside the declaration is a bad
idea, even if limited to TU.
Very deep thoughts, I am not sure I fully understand them.
It did my best to interpret. This is what I came up with:
The main source of frustration seems to come from the way we store and
compile c++ code.
We break it into smaller pieces both for organization purposes, but also to
be able to feed them more or less independently to the compiler (e.g.
incremental recompile).
The language has to acknowledge that, and hence we have the definition of a
translation unit, and a set of rules attached to it.
However, in a perfect world we wouldn't need to rely on this, so therefore
we should refrain from language rules grounded on this concept where
possible.
(I know that these are very hard statements. They reflect my current
understanding and are not objective truths. Please educate me if I am way
off.)
Therefore it can be argued that all translation units should agree on a
class' interface (in our case, what methods it does or does not have).
We just established that it is also reasonable to relax this statement; not
all TUs need to agree on the private interface of a class, and
your statement is that "they should agree on the rest though".
This sounds rather reasonable to me.
So what if we try to follow this principle as closely as possible?
- All TUs should agree on public/protected members -- we don't plan to
break this.
- TUs can be denied from knowing the private member methods of a class (on
a need-to-know basis)... -- we all seem to agree on this so far --
- ..., but those that need to know should all agree on the private
interface as well. (?)
Maybe this would solve some ODR/overload resolution issues?
Your reasoning did convince me to drop the 'new keyword/annotation' syntax,
and I see there is already a term coined for my preferred way: 'reopening
the private scope (explicitly)'.
With all of this in mind, I came up with the following solution:
=== MyClass.hpp ===
class MyClass { *public interface* };
=== MyClass_private.hpp ===
#include "MyClass.hpp"
class private MyClass { *private interface* };
=== MyClass_impl_1.cpp ===
#include "MyClass_private.hpp"
...
=== MyClass_impl_2.cpp ===
#include "MyClass_private.hpp"
...
Obviously the MyClass_private.hpp header can be omitted if all private
implementation details are confined to a single .cpp file.
To me this way of doing it feels less 'divergent' from the current standard.
It looks and feels more familiar to the C++ I know.
IMHO it is easier to explain to a newcomer, because the rules are exactly
the same for regular class definitions.
Also I read the paper on github, and found the following:
> As a side note, some programming languages provide a mixin feature which
allows programmers to reopen a class and extend its interface arbitrarily.
> We are not proposing or even endorsing any form of mixin here.
> Adding, removing, or changing private methods does not change the
interface of the class.
Isn't the originally proposed way essentially the same?
This private extension method definition:
private void MyClass::Foo() { ... }
in my mind is equivalent to reopening the private scope and 'mixing in' a
new method:
class private MyClass {
void Foo() { ... }
};
except the first syntax is just doing it 'sneakily' (implicitly).
Am I missing something?
Sincerely,
Matthew
On Wed, 29 Apr 2026 at 09:20, Sebastian Wittmeier via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Hi André,
>
> I think you got the point itself.
>
> As Thiago pointed out, it does not have to be necessarily templates.
>
> It probably is enough, if the member function is defined inside the header
> file, e.g. directly within the declaration of the class or marked as inline.
>
>
>
> Then overload resolution could differ even for calls (to private member
> functions) within the class.
>
>
>
> Currently there are already possible ODR violations for calls outside the
> class, if a different (e.g. free) function is called.
>
>
>
> Internal linkage, which should protect the class encapsulation could give
> the wrong picture about those private extension functions. Shifting the
> image from unique and declared upfront per class to declared per TU and not
> only private in the class, but also exclusive to the TU.
>
>
>
> The mental picture should still be that all private functions are clearly
> separate from each other (even over several TUs). And if they have the same
> name/signature, then they should not be called by any of the functions
> defined in a header file: templates, functions defined in the declaration
> and inlined functions.
>
>
>
>
> -----Ursprüngliche Nachricht-----
> *Von:* André Offringa via Std-Proposals <std-proposals_at_[hidden]>
> *Gesendet:* Mi 29.04.2026 08:59
> *Betreff:* Re: [std-proposals] Translation-unit-local functions that
> access private class fields
> *An:* std-proposals_at_[hidden];
> *CC:* André Offringa <offringa_at_[hidden]>;
> On 4/29/26 12:40 AM, Thiago Macieira via Std-Proposals wrote:
> > On Tuesday, 28 April 2026 15:12:26 Pacific Daylight Time Sebastian
> Wittmeier
> > via Std-Proposals wrote:
> >> Could template member functions, declared in the class together with
> private
> >> extension functions, which change overload resolution, be a small issue?
> > Yes, that could cause an ODR violation. You don't need templates for that
> > either. Plus, you can get violations without overloads, by using requires
> > clauses.
>
> Is the following an example of what you mean with ODR violation?
>
> == Header ==
>
> template<typename T>
> class Foo {
> public:
> Foo() {
> A(T()); // dependent lookup, overload resolved at instantiation
> }
>
> private:
> void A(double);
> // void A(int); -> not declared, extended later
> }
>
> // If Foo instantiated here -> A(int) does not participate
>
> == cpp ==
>
> private void Foo::A(int) {
> }
>
> // If Foo instantiated here -> A(int) does participate
>
> So if in one translation unit Foo is instantiated without seeing A(int)
> and in another
> Foo is instantiated with seeing A(int), it would be ill-formed (IFNDR).
>
> If you were thinking of a different problem, could you give an example?
>
> I think this problem is similar as that is already the case with free
> functions, isn't it?
> Though maybe this trap is slightly more easy to trigger... not sure. At
> the very least,
> this problem didn't exist for class members before and now it does, so I
> think that's
> worthwhile to mention in the proposal. Good points, thanks!
>
> Regards,
> André
>
> --
> 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
>
> C++ has 2 important boundaries
> - translation unit: internal vs. external linkage
> and
> - classes
> - together with namespaces (a class cannot be in two sibling namespaces
at the same time)
> [Orthogonal concepts:
> - block scopes have to be inside both
> - With the preprocessor one TU can have several headers, but one header
can also be in several TUs
> - similar modules]
> With templates and inline code TUs are a bad protection for encapsulation.
> So creating public member functions outside the declaration is a bad
idea, even if limited to TU.
Very deep thoughts, I am not sure I fully understand them.
It did my best to interpret. This is what I came up with:
The main source of frustration seems to come from the way we store and
compile c++ code.
We break it into smaller pieces both for organization purposes, but also to
be able to feed them more or less independently to the compiler (e.g.
incremental recompile).
The language has to acknowledge that, and hence we have the definition of a
translation unit, and a set of rules attached to it.
However, in a perfect world we wouldn't need to rely on this, so therefore
we should refrain from language rules grounded on this concept where
possible.
(I know that these are very hard statements. They reflect my current
understanding and are not objective truths. Please educate me if I am way
off.)
Therefore it can be argued that all translation units should agree on a
class' interface (in our case, what methods it does or does not have).
We just established that it is also reasonable to relax this statement; not
all TUs need to agree on the private interface of a class, and
your statement is that "they should agree on the rest though".
This sounds rather reasonable to me.
So what if we try to follow this principle as closely as possible?
- All TUs should agree on public/protected members -- we don't plan to
break this.
- TUs can be denied from knowing the private member methods of a class (on
a need-to-know basis)... -- we all seem to agree on this so far --
- ..., but those that need to know should all agree on the private
interface as well. (?)
Maybe this would solve some ODR/overload resolution issues?
Your reasoning did convince me to drop the 'new keyword/annotation' syntax,
and I see there is already a term coined for my preferred way: 'reopening
the private scope (explicitly)'.
With all of this in mind, I came up with the following solution:
=== MyClass.hpp ===
class MyClass { *public interface* };
=== MyClass_private.hpp ===
#include "MyClass.hpp"
class private MyClass { *private interface* };
=== MyClass_impl_1.cpp ===
#include "MyClass_private.hpp"
...
=== MyClass_impl_2.cpp ===
#include "MyClass_private.hpp"
...
Obviously the MyClass_private.hpp header can be omitted if all private
implementation details are confined to a single .cpp file.
To me this way of doing it feels less 'divergent' from the current standard.
It looks and feels more familiar to the C++ I know.
IMHO it is easier to explain to a newcomer, because the rules are exactly
the same for regular class definitions.
Also I read the paper on github, and found the following:
> As a side note, some programming languages provide a mixin feature which
allows programmers to reopen a class and extend its interface arbitrarily.
> We are not proposing or even endorsing any form of mixin here.
> Adding, removing, or changing private methods does not change the
interface of the class.
Isn't the originally proposed way essentially the same?
This private extension method definition:
private void MyClass::Foo() { ... }
in my mind is equivalent to reopening the private scope and 'mixing in' a
new method:
class private MyClass {
void Foo() { ... }
};
except the first syntax is just doing it 'sneakily' (implicitly).
Am I missing something?
Sincerely,
Matthew
On Wed, 29 Apr 2026 at 09:20, Sebastian Wittmeier via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> Hi André,
>
> I think you got the point itself.
>
> As Thiago pointed out, it does not have to be necessarily templates.
>
> It probably is enough, if the member function is defined inside the header
> file, e.g. directly within the declaration of the class or marked as inline.
>
>
>
> Then overload resolution could differ even for calls (to private member
> functions) within the class.
>
>
>
> Currently there are already possible ODR violations for calls outside the
> class, if a different (e.g. free) function is called.
>
>
>
> Internal linkage, which should protect the class encapsulation could give
> the wrong picture about those private extension functions. Shifting the
> image from unique and declared upfront per class to declared per TU and not
> only private in the class, but also exclusive to the TU.
>
>
>
> The mental picture should still be that all private functions are clearly
> separate from each other (even over several TUs). And if they have the same
> name/signature, then they should not be called by any of the functions
> defined in a header file: templates, functions defined in the declaration
> and inlined functions.
>
>
>
>
> -----Ursprüngliche Nachricht-----
> *Von:* André Offringa via Std-Proposals <std-proposals_at_[hidden]>
> *Gesendet:* Mi 29.04.2026 08:59
> *Betreff:* Re: [std-proposals] Translation-unit-local functions that
> access private class fields
> *An:* std-proposals_at_[hidden];
> *CC:* André Offringa <offringa_at_[hidden]>;
> On 4/29/26 12:40 AM, Thiago Macieira via Std-Proposals wrote:
> > On Tuesday, 28 April 2026 15:12:26 Pacific Daylight Time Sebastian
> Wittmeier
> > via Std-Proposals wrote:
> >> Could template member functions, declared in the class together with
> private
> >> extension functions, which change overload resolution, be a small issue?
> > Yes, that could cause an ODR violation. You don't need templates for that
> > either. Plus, you can get violations without overloads, by using requires
> > clauses.
>
> Is the following an example of what you mean with ODR violation?
>
> == Header ==
>
> template<typename T>
> class Foo {
> public:
> Foo() {
> A(T()); // dependent lookup, overload resolved at instantiation
> }
>
> private:
> void A(double);
> // void A(int); -> not declared, extended later
> }
>
> // If Foo instantiated here -> A(int) does not participate
>
> == cpp ==
>
> private void Foo::A(int) {
> }
>
> // If Foo instantiated here -> A(int) does participate
>
> So if in one translation unit Foo is instantiated without seeing A(int)
> and in another
> Foo is instantiated with seeing A(int), it would be ill-formed (IFNDR).
>
> If you were thinking of a different problem, could you give an example?
>
> I think this problem is similar as that is already the case with free
> functions, isn't it?
> Though maybe this trap is slightly more easy to trigger... not sure. At
> the very least,
> this problem didn't exist for class members before and now it does, so I
> think that's
> worthwhile to mention in the proposal. Good points, thanks!
>
> Regards,
> André
>
> --
> 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
>
-- Üdv.: Máté
Received on 2026-04-29 09:51:36
