Operadores de comparação para uma classe em C++23 podem ter parâmetros de objeto explícitos de um tipo distinto do tipo de classe?
Considere por exemplo
struct A {
int i;
constexpr bool operator==(this int x, int y) { return x == y; }
constexpr operator int() const { return i; }
};
Agora a comparação para desigualdade
static_assert( A{0} != A{1} );
é aceito pelo GCC e Clang, mas o MSVC reclama:
error C2803: 'operator ==' must have at least one formal parameter of class type
error C2333: 'A::operator ==': error in function declaration; skipping function body
E a comparação para igualdade
static_assert( A{2} == A{2} );
é aceito apenas pelo GCC, enquanto o Clang já não gosta dele:
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)
Demonstração online: https://gcc.godbolt.org/z/dnKc1fhcT
Qual compilador está correto aqui?
Até onde eu sei:
O erro do MSVC está incorreto. [over.oper.general]/7 coloca requisitos nos tipos de parâmetros somente para sobrecargas não-membros . Uma função de parâmetro de objeto explícita é uma função membro.
A mensagem de erro do Clang está correta. Há um candidato interno
operator==(int, int)
para==
, que não é uma correspondência melhor ou pior do que suaoperator==
sobrecarga por nenhuma das desambiguações de sobrecarga. Há [over.match.oper]/3.3.4 que exclui um candidato interno se ele tiver uma lista de tipos de parâmetros correspondente com um dos candidatos não membros, mas, novamente, apenas para candidatos não membros e a sobrecarga com parâmetro de objeto explícito é um candidato membro.Caso
static_assert( A{0} != A{1} );
não haja problema, porque isso vai preferir o candidato embutidooperator!=(int, int)
em vez de qualquer candidato reescrito que use suaoperator==
sobrecarga. Tanto o GCC quanto o Clang ignoram sua sobrecarga.Isso significaria que tais sobrecargas são permitidas, mas de uso prático limitado, porque elas serão, na maioria das vezes, ambíguas com o candidato integrado. Talvez [over.match.oper]/3.3.4 deva se aplicar a funções de membro também, comparando a lista de parâmetros com parâmetros de objeto em vez disso.