Static and non-static member function templates with the same parameter types and requires clause in C++
Asked Answered
H

3

17

Static and non-static member functions with the same parameter types cannot be overloaded. But if member functions are templates and one of them has requires clause then all compilers allow it. But the problems appear when one calls both of the member functions:

struct A {
    static int f(auto) { return 1; }
    int f(auto) requires true { return 2; }
};

int main() {
    [[maybe_unused]] int (A::*y)(int) = &A::f; // ok everywhere (if no below line)
    [[maybe_unused]] int (*x)(int) = &A::f; //ok in GCC and Clang (if no above line)
}

If only one (any) line is left in main() then GCC and Clang accept the program. But when both lines in main() are present, Clang prints

error: definition with same mangled name '_ZN1A1fIiEEiT_' as another definition

and GCC reports an internal compiler error. Demo: https://gcc.godbolt.org/z/4c1z7fWvx

Are all compilers wrong in accepting struct A definition with overloaded static and not-static member functions? Or they just have similar bugs in the calling of both functions.

Hyssop answered 31/12, 2021 at 12:19 Comment(2)
This is essentially a duplicate of #70430041 unless I'm missing something.Lester
Thanks for the reference. In that question the answer is that Clang is correct, which GCC is not, while here both shows the same behavior.Hyssop
H
1

This is valid code as per [over.over]p4 which says:

Non-member functions, static member functions, and explicit object member functions match targets of function pointer type or reference to function type. Implicit object member functions match targets of pointer-to-member-function type.

The error that clang and gcc produce are mangling bugs see clang bug: function signature constraints are not a part of mangled name which was just fixed and gcc bug: function signature constraints are not a part of mangled name.

Horseflesh answered 20/9, 2023 at 20:12 Comment(0)
T
1

This is quite messy! While skimming through the standard, I found this much simpler example in [over.over]:

struct X {
  int f(int);
  static int f(long);
};

int (X::*p1)(int)  = &X::f;     // OK
int    (*p2)(int)  = &X::f;     // error: mismatch
int    (*p3)(long) = &X::f;     // OK

So I first tried the two valid lines, but even they are rejected (independently of each other) by all three compilers. That is a problem.

Looking further, about the address of function templates, [temp.deduct.funcaddr] says:

Template arguments can be deduced from the type specified when taking the address of an overload set. If there is a target, the function template's function type and the target type are used as the types of P and A, and the deduction is done as described in [temp.deduct.type]. Otherwise, deduction is performed with empty sets of types P and A.

Such targets are described in [over.over], and in your example are of the first type:

(1.1) an object or reference being initialized ([dcl.init], [dcl.init.ref], [dcl.init.list]),

Even before considering any constraints, it seems to me that a compiler should be able to identify a unique candidate in both of your cases, just as in the simpler examples, given that the targets have different signatures.

Tobiastobie answered 1/1, 2022 at 23:12 Comment(0)
H
1

This is valid code as per [over.over]p4 which says:

Non-member functions, static member functions, and explicit object member functions match targets of function pointer type or reference to function type. Implicit object member functions match targets of pointer-to-member-function type.

The error that clang and gcc produce are mangling bugs see clang bug: function signature constraints are not a part of mangled name which was just fixed and gcc bug: function signature constraints are not a part of mangled name.

Horseflesh answered 20/9, 2023 at 20:12 Comment(0)
H
0

According to the latest draft of 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. So the definition of struct A is malformed in C++20.

The same item was rephrased In the first draft of C++23, class.static.mfct#2

There cannot be a static and a non-static member function with the same name, parameter-type-list, and trailing requires-clause ([over.load]).

According to this, the definition of A is already fine. And it looks like GCC, Clang, and MSVC all follow this statement even in C++20 mode. And the errors we observe happen because of same mangled names of both functions (which hardly can be fixed preserving the current ABI).

And in the latest draft of C++, class.static.mfct#2 any restriction on having static and not-static member functions with same name and parameters are removed.

Hyssop answered 4/1, 2022 at 13:26 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.