Search code examples
c++language-lawyercomparison-operatorsc++23explicit-object-parameter

Comparison operator with explicit object parameter of not class type


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?


Solution

  • 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.