Могут ли операторы сравнения для класса в C++23 иметь явный объектный параметр типа, отличного от типа класса?
Рассмотрим, например
struct A {
int i;
constexpr bool operator==(this int x, int y) { return x == y; }
constexpr operator int() const { return i; }
};
Теперь сравнение неравенства
static_assert( A{0} != A{1} );
принимается GCC и Clang, но MSVC жалуется:
error C2803: 'operator ==' must have at least one formal parameter of class type
error C2333: 'A::operator ==': error in function declaration; skipping function body
И сравнение на равенство
static_assert( A{2} == A{2} );
принимается только GCC, а Clang уже не нравится:
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)
Онлайн-демо: https://gcc.godbolt.org/z/dnKc1fhcT
Какой компилятор здесь прав?
Насколько я могу сказать:
Ошибка MSVC неверна. [over.oper.general]/7 предъявляет требования к типам параметров только для перегрузок, не являющихся членами. Явная функция параметра объекта является функцией-членом.
Сообщение об ошибке Clang верное. Существует встроенный кандидат operator==(int, int)
для ==
, который не лучше и не хуже вашей перегрузки operator==
ни по одному из значений перегрузки. Существует [over.match.oper]/3.3.4, который исключает встроенного кандидата, если он имеет список типов параметров, совпадающий с одним из кандидатов, не являющихся членами, но опять же, только для кандидатов, не являющихся членами, и перегрузка с явным параметром объекта является кандидатом на членство.
В случае static_assert( A{0} != A{1} );
проблем нет, потому что встроенный кандидат operator!=(int, int)
будет предпочтительнее любого переписанного кандидата, который использует вашу перегрузку operator==
. И GCC, и Clang игнорируют вашу перегрузку.
Это означало бы, что такие перегрузки разрешены, но имеют ограниченное практическое применение, поскольку в большинстве случаев они будут неоднозначными для встроенного кандидата. Возможно, [over.match.oper]/3.3.4 следует применить и к функциям-членам, вместо этого сравнивая список параметров с параметрами объекта.