Considere este ejemplo válido de C++17:
struct A {
bool operator==(const A&);
};
int main() {
return A{} == A{};
}
Cuando compilado en clang con -std=c++20 da:
<source>:7:15: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'A' and 'A') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
return A{} == A{};
~~~ ^ ~~~
<source>:2:9: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
bool operator==(const A&);
¿Significa esta advertencia que C++20 no permite el uso de un operador de comparación típico para comparar dos objetos del mismo tipo? ¿Cuál es la alternativa correcta? ¿Se espera que la situación cambie en futuros borradores?
¿Significa esta advertencia que C++20 no permite el uso de un operador de comparación típico para comparar dos objetos del mismo tipo? ¿Cuál es la alternativa correcta? ¿Se espera que la situación cambie en futuros borradores?
Este no es realmente un operador de comparación típico, ya es un poco incorrecto, ya que solo permite un const
objeto en un lado (su tipo A
no satisfaría el nuevo equality_comparable
concepto tampoco, incluso sin cambios de idioma).
Tienes que escribirlo de esta manera:
struct A {
bool operator==(const A&) const;
// ^^^^^^
};
Esta es la regla final para C++20.
El problema específico es que en C++20, los operadores de comparación agregan una nueva noción de candidatos reescritos e invertidos. Así que busca la expresión a == b
también terminará haciendo coincidir operadores como b == a
. En el caso típico, esto significa que tienes que escribir menos operadores, ya que sabemos que la igualdad es conmutativa.
Pero si tiene un desajuste constante, lo que sucede es que termina con estos dos candidatos:
bool operator==(/* this*/ A&, A const&); // member function
bool operator==(A const&, /* this*/ A&); // reversed member function
Con dos argumentos de tipo A
. El primer candidato es mejor en el primer argumento y el segundo candidato es mejor en el segundo argumento. Ninguno de los candidatos es mejor que el otro, por lo tanto, ambiguo.
Es una regla general de resolución de sobrecarga que cada tipo de argumento debe ser por separado al menos tan cerca del tipo de parámetro de una función seleccionada como del tipo de parámetro de cualquier otra:
struct A {A(int);};
void f(long,int); // #1
void f(int,A); // #2
void g() {f(0,0);} // error: ambiguous
La conversión mucho peor para el segundo argumento para el n. ° 2 no compensa el int
→long
conversión en el primer argumento.
En C++20, se agregaron varias reglas de reescritura para obviar la necesidad de escribir tantas sobrecargas de operadores de comparación casi idénticas. Mientras que las ambigüedades triviales entre los “candidatos invertidos” escritos a mano y los generados por compiladores idénticos son manejados por desempate reglas que prefieren funciones reales, eso (nuevamente) no es suficiente para compensar una conversión peor para cualquier argumento.
Los operadores de comparación escritos cuidadosamente de acuerdo con las prácticas aceptadas (C ++ 17) rara vez entrarán en conflicto con esto, pero cuestionable firmas como esta (con asimétrica const
) puede muy bien ser problemático (en nuevas formas). Esperemos que se encuentren más errores de los causados por esta extensión.
Dejaré los detalles a un abogado de idiomas, pero haciendo la función
const
debe dejar que la advertencia se evapore. creo que existe el aviso, ya que invocasoperator==
en un temporal.– aep
25 de febrero de 2020 a las 2:45
@aep Es cierto que la advertencia desaparece agregando
const
pero no quitando el temporal (A a, b; a == b
). Gracias, supongo que debe estar relacionado con un lado de la==
siendo const pero no el otro.– Falta
25 de febrero de 2020 a las 3:03
No hay más cambios para C++20 (excepto los que aún se están fusionando desde la última reunión para esa versión).
– Davis arenque
25 de febrero de 2020 a las 3:13
Lo juro, la mitad de la razón por la que prefiero seguir la pauta de “usar sobrecargas de no miembros” es que es muy fácil olvidarse de declarar la función miembro en sí
const
donde la función que no es miembro solo necesita recordar declarar los argumentosconst
.– ShadowRanger
25 de febrero de 2020 a las 3:25