Date: Thu, 30 Apr 2026 18:06:38 +0200
On 4/30/26 01:25, Rhidian De Wit via Std-Proposals wrote:
> [..]
> Maybe PEM's should not be allowed to be overloaded with non-static
> member functions of the class? As in, the /private void Foo::F(int)
> {}/ declaration would give a compiler error as it overloads a
> non-static member function of /Foo/. This would avoid confusion with
> programmers (and make teaching this more simple) as otherwise there'd
> be the need to explain why /F(5)/ is resolving to /F(double)/ rather
> than /F(int)/, which is the better fitting match.
Thanks for all the input. I have some new questions. Not allowing
overloading with private extension member functions could indeed be a
way to prevent the addition of new ways of violating ODR with this
proposal. On the other hand, it would make the feature slightly less
strong, overall.
I see four possible options:
1) Private extension member functions (PEMFs) may not overload at all.
It is a simple rule, and a relatively consistent rule but constrains the
feature the most. I believe this solves all newly created ODR issues.
2) PEMFs may only overload other PEMFs.
This is slightly less simple and slightly less consistent (could produce
"why is this allowed and that not?!" reactions), but also slightly less
constraining, and allows reusing overloaded functions and extending them
(see below). This solves most ODR issues, and I think only leaves very
rare contrived ways of breaking ODR not much different from what
functions can already do (e.g. for a template class, declare one PEMF
overload in the header file, one in the implementation file and cause it
to be instantiated after the header file + after the 2nd PEMF).
3) As option 2), but also allow overloading constructors.
Constructors are special, and PEMF constructors might be useful despite
restrictions (example below). PEMF constructors can still break ODR.
4) Allow (all) overloading.
The result is that PEMFs can introduce new ways of violating ODR, but
allowing this allows for slightly more flexibility in the class design.
I think the 2nd option is a good compromise. I feel that if we allow
overloading, the way that PEMFs can break ODR by introducing overloads
is not thát trivial to avoid, i.e. one might break ODR without realizing
it. If we go for option 4, I could imagine that a "best practice"
becomes "never overload member functions with PEMFs", so if so, why not
enforce it? The loss in functionality of overloading seems relatively
small, but is not negligible either.
Both options 1) and 2) mean that a (delegated) constructors can never be
a PEMFs. I think that it's somewhat rare that a delegated constructor
would add dependencies, so the motivation for a PEMF constructor is I
think small. That said, it's not unthinkable that one wants to
initialize the class data members using a delegated constructor, and
that for that the constructor needs to have a signature that has
dependencies. Without PEMF constructors, these dependencies are then
part of the class declaration. I think that PEMF constructors are thus
more desirable than regular function overloading.
With option 2, the functionality of regular function overloading could
still be created by declaring PEMF overloads with a new name that fwds
to the non-PEMF for types that it doesn't extend. This way, an
overloaded function with extension can be made.
A PEMF constructor can break ODR just as a regular function, so I think
option 3 makes less sense, and is I think the worst balance between
expressiveness and limiting surprises.
When we taking a safe approach (1 or 2) which is found to be too
constraining after all, it could be changed to a later option in the
list, without backward breaking changes.
My questions:
- Do people agree option 2 is most sensible, or am I overlooking something?
- Would disallowing overloads (option 1) indeed stop all new ways of
breaking the ODR allowed by this proposal, and does option 2 indeed stop
all but contrived ODR breaks?
Regards,
André
> [..]
> Maybe PEM's should not be allowed to be overloaded with non-static
> member functions of the class? As in, the /private void Foo::F(int)
> {}/ declaration would give a compiler error as it overloads a
> non-static member function of /Foo/. This would avoid confusion with
> programmers (and make teaching this more simple) as otherwise there'd
> be the need to explain why /F(5)/ is resolving to /F(double)/ rather
> than /F(int)/, which is the better fitting match.
Thanks for all the input. I have some new questions. Not allowing
overloading with private extension member functions could indeed be a
way to prevent the addition of new ways of violating ODR with this
proposal. On the other hand, it would make the feature slightly less
strong, overall.
I see four possible options:
1) Private extension member functions (PEMFs) may not overload at all.
It is a simple rule, and a relatively consistent rule but constrains the
feature the most. I believe this solves all newly created ODR issues.
2) PEMFs may only overload other PEMFs.
This is slightly less simple and slightly less consistent (could produce
"why is this allowed and that not?!" reactions), but also slightly less
constraining, and allows reusing overloaded functions and extending them
(see below). This solves most ODR issues, and I think only leaves very
rare contrived ways of breaking ODR not much different from what
functions can already do (e.g. for a template class, declare one PEMF
overload in the header file, one in the implementation file and cause it
to be instantiated after the header file + after the 2nd PEMF).
3) As option 2), but also allow overloading constructors.
Constructors are special, and PEMF constructors might be useful despite
restrictions (example below). PEMF constructors can still break ODR.
4) Allow (all) overloading.
The result is that PEMFs can introduce new ways of violating ODR, but
allowing this allows for slightly more flexibility in the class design.
I think the 2nd option is a good compromise. I feel that if we allow
overloading, the way that PEMFs can break ODR by introducing overloads
is not thát trivial to avoid, i.e. one might break ODR without realizing
it. If we go for option 4, I could imagine that a "best practice"
becomes "never overload member functions with PEMFs", so if so, why not
enforce it? The loss in functionality of overloading seems relatively
small, but is not negligible either.
Both options 1) and 2) mean that a (delegated) constructors can never be
a PEMFs. I think that it's somewhat rare that a delegated constructor
would add dependencies, so the motivation for a PEMF constructor is I
think small. That said, it's not unthinkable that one wants to
initialize the class data members using a delegated constructor, and
that for that the constructor needs to have a signature that has
dependencies. Without PEMF constructors, these dependencies are then
part of the class declaration. I think that PEMF constructors are thus
more desirable than regular function overloading.
With option 2, the functionality of regular function overloading could
still be created by declaring PEMF overloads with a new name that fwds
to the non-PEMF for types that it doesn't extend. This way, an
overloaded function with extension can be made.
A PEMF constructor can break ODR just as a regular function, so I think
option 3 makes less sense, and is I think the worst balance between
expressiveness and limiting surprises.
When we taking a safe approach (1 or 2) which is found to be too
constraining after all, it could be changed to a later option in the
list, without backward breaking changes.
My questions:
- Do people agree option 2 is most sensible, or am I overlooking something?
- Would disallowing overloads (option 1) indeed stop all new ways of
breaking the ODR allowed by this proposal, and does option 2 indeed stop
all but contrived ODR breaks?
Regards,
André
Received on 2026-04-30 16:06:43
