Is this code valid?
template<bool b>
struct s {
void f() const {
}
static void f() requires b {
}
};
void g() {
s<true>().f();
}
clang says yes, but gcc says no
<source>: In function 'void g()':
<source>:10:20: error: call of overloaded 'f()' is ambiguous
10 | s<true>().f();
| ~~~~~~~~~~~^~
<source>:3:14: note: candidate: 'void s<b>::f() const [with bool b = true]'
3 | void f() const {
| ^
<source>:5:21: note: candidate: 'static void s<b>::f() requires b [with bool b = true]'
5 | static void f() requires b {
| ^
Compiler returned: 1
https://godbolt.org/z/f4Kb68aee
2) Member function declarations with the same name and the name parameter-type-list cannot be overloaded if any of them is a static member function declaration.
What happens if non static members are used in static member function? Explanation: There must be specific memory space allocated for the data members before the static member functions uses them. But the space is not reserved if object is not declared.
Function declarations that differ only in the return type cannot be overloaded. Member function declarations with the same name and the same parameter types cannot be overloaded if any of them is a static member function declaration (9.4).
A static member function can be called, even when a class is not instantiated. A static member function cannot have access to the this pointer of the class. A non-static member function can be declared as virtual but care must be taken not to declare a static member function as virtual.
Function declarations that differ only in the return type cannot be overloaded. Member function declarations with the same name and the same parameter types cannot be overloaded if any of them is a static member function declaration (9.4).
A non-static member function can be declared to take as its first parameter an explicit object parameter, denoted with the prefixed keyword this . For template member functions, explicit object parameter allows deduction of type and value category, this language feature is called "deducing this "
C++ allows member methods to be overloaded on the basis of const type. Overloading on the basis of const type can be useful when a function return reference or pointer.
During overload resolution, non-static member function with a cv-qualifier sequence of class X is treated as follows: no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument
If we go through [over.match.best.general], we get:
a viable function
F1
is defined to be a better function than another viable functionF2
if for all argumentsi
,ICSi(F1)
is not a worse conversion sequence thanICSi(F2)
, and then [...]
The only argument is the object argument, and we have earlier that:
If
F
is a static member function,ICS1(F)
is defined such thatICS1(F)
is neither better nor worse thanICS1(G)
for any functionG
, and, symmetrically,ICS1(G)
is neither better nor worse thanICS1(F)
; otherwise,
So the premise holds: all arguments for one function have a conversion sequence no worse than the conversion sequence for the other function. So we move on to our tiebreakers...
- for some argument
j
,ICSj(F1)
is a better conversion sequence thanICSj(F2)
, or, if not that,
The only argument that we could have a better conversion sequence for is the object argument, and as established, that one is equivalent. So this tiebreaker does not apply.
- the context is an initialization by user-defined conversion (see [dcl.init], [over.match.conv], and [over.match.ref]) and [...]
Nope.
- the context is an initialization by conversion function for direct reference binding of a reference to function type, [...]
Nope.
F1
is not a function template specialization andF2
is a function template specialization, or, if not that,
Nope.
F1
andF2
are function template specializations, and the function template forF1
is more specialized than the template forF2
according to the partial ordering rules described in [temp.func.order], or, if not that,
Nope.
F1
andF2
are non-template functions with the same parameter-type-lists, andF1
is more constrained thanF2
according to the partial ordering of constraints described in [temp.constr.order], or if not that,
Aha! In this example, we have non-template functions with the same parameter-type-lists (both are just empty). The static member function is constrained and the non-static member function is not constrained, which is the most trivial kind of "more constrained" (see [temp.constr.order]).
As such, I think that clang (and msvc) are correct to accept the program and gcc is incorrect to reject it. (submitted 103783).
Your code is ill-formed according to C++20 standard class.static.mfct#2:
There shall not be a static and a non-static member function with the same name and the same parameter types ([over.load]).
There is no exception here for the presence of requires
-clause to differentiate member functions, only same name and the same parameter types. And it is exactly our case: the same name is f
, and the same parameter types is empty set.
So Clang and MSVC are wrong in accepting the code. But the diagnostics of GCC is definitely confusing.
With some minor tweaks in the code (removed const
in not-static member function and get its address in the code), Clang and MSVC also show to have big problems with it:
template<bool b>
struct s {
void f() {}
static void f() requires b {}
};
int main() {
s<true>().f();
void (s<true>::*x)() = &s<true>::f;
}
Demo: https://gcc.godbolt.org/z/vdq9j63Gs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With