Can comparison operators for a class in C++23 have explicit object parameter of a type distinct from the class type?
Consider for example
struct A {
int i;
constexpr bool operator==(this int x, int y) { return x == y; }
constexpr operator int() const { return i; }
};
Now the comparison for inequality
static_assert( A{0} != A{1} );
is accepted by GCC and Clang, but MSVC complains:
error C2803: 'operator ==' must have at least one formal parameter of class type
error C2333: 'A::operator ==': error in function declaration; skipping function body
And the comparison for equality
static_assert( A{2} == A{2} );
is accepted by GCC only, while Clang already dislikes it:
error: use of overloaded operator '==' is ambiguous (with operand types 'A' and 'A')
11 | static_assert( A{2} == A{2} );
note: candidate function
3 | constexpr bool operator==(this int x, int y) { return x == y; }
note: built-in candidate operator==(int, int)
Online demo: https://gcc.godbolt.org/z/dnKc1fhcT
Which compiler is correct here?
As far as I can tell:
MSVC's error is incorrect. [over.oper.general]/7 places requirements on the types of parameters only for non-member overloads. An explicit object parameter function is a member function. (See also CWG 2931. The current suggested resolution would make MSVC's behavior correct instead.)
Clang's error message is correct. There is a built-in operator==(int, int)
candidate for ==
, which is not a better or worse match than your operator==
overload by any of the overload disambiguations. There is [over.match.oper]/3.3.4 which excludes a built-in candidate if it has a matching parameter type list with one of the non-member candidates, but again, only for non-member candidates and the overload with explicit object parameter is a member candidate.
In case of static_assert( A{0} != A{1} );
there is no problem, because this will prefer the built-in operator!=(int, int)
candidate over any rewritten candidate that uses your operator==
overload. Both GCC and Clang ignore your overload.
This would mean that such overloads are permitted, but of limited practical use, because they will mostly be ambiguous with the built-in candidate. Maybe [over.match.oper]/3.3.4 should apply to member functions as well, comparing the parameter list with object parameters instead.