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é
--