c++ - sencillos - Sobrecargas de operadores de igualdad: ¿Es(x!=Y)==(!(X== y))?
sobrecarga de operadores en c++ ejemplos sencillos (3)
¿Garantiza el estándar de C ++ que
(x!=y)
siempre tiene el mismo valor de verdad que!(x==y)
?
No, no lo hace. Absolutamente nada me impide escribir:
struct Broken {
bool operator==(const Broken& ) const { return true; }
bool operator!=(const Broken& ) const { return true; }
};
Broken x, y;
Es un código perfectamente bien formado. Semánticamente, está roto (como podría sugerir el nombre), pero ciertamente no hay nada de malo en ello desde una perspectiva de funcionalidad de código C ++ puro.
El estándar también indica claramente que esto está bien en [over.oper]/7 :
Las identidades entre ciertos operadores predefinidos aplicados a tipos básicos (por ejemplo,
++a ≡ a+=1
) no tienen que ser válidas para las funciones del operador. Algunos operadores predefinidos, como+=
, requieren que un operando sea un valor l cuando se aplica a los tipos básicos; Esto no es requerido por las funciones del operador.
De la misma manera, nada en el estándar de C ++ garantiza que el operator<
realidad implemente un pedido válido (o que x<y <==> !(x>=y)
, etc.). Algunas implementaciones de bibliotecas estándar en realidad agregarán instrumentación para intentar depurar esto por usted en los contenedores ordenados, pero eso es solo un problema de calidad de implementación y no una decisión basada en estándares.
Las soluciones de biblioteca como los operadores de impulso existen para al menos hacer esto un poco más fácil para el programador:
struct Fixed : equality_comparable<Fixed> {
bool operator==(const Fixed&) const;
// a consistent operator!= is provided for you
};
Aunque ahora Fixed
ya no puede ser un agregado. Así que todavía no es una solución ideal. Aunque P0017 aparentemente ha sido aceptado para C ++ 17, lo que permitiría la inicialización de listas para Fixed
.
Espero que esto no sea un duplicado: busqué otras preguntas relacionadas con los operadores de igualdad, pero aparte de algunos comentarios en preguntas relacionadas, no encontré una declaración definitiva
tl; dr:
¿Garantiza el estándar de C ++ que (x!=y)
siempre tiene el mismo valor de verdad que !(x==y)
?
Sé que hay muchas sutilezas involucradas aquí: los operadores ==
y !=
Pueden estar sobrecargados. Pueden estar sobrecargados para tener diferentes tipos de devolución (que solo tienen que ser convertibles implícitamente a bool
). Incluso el !
-operador podría estar sobrecargado en el tipo de retorno. Es por eso que me referí de forma manual al "valor de verdad" anterior, pero tratando de desarrollarlo más, explotando la conversión implícita a bool
e intentando eliminar posibles ambigüedades:
bool ne = (x!=y);
bool e = (x==y);
bool result = (ne == (!e));
¿Se garantiza que el result
sea true
aquí?
El estándar C ++ especifica los operadores de igualdad en la sección 5.10, pero parece definirlos principalmente de manera sintáctica (y algunas semánticas con respecto a las comparaciones de punteros). El concepto de ser EqualityComparable existe, pero no hay una declaración dedicada sobre la relación de su operador ==
con el operador !=
.
Existen documentos relacionados de los grupos de trabajo de C ++ , que dicen que ...
Es vital que los comportamientos iguales / [...] desiguales sean negaciones booleanas entre sí. Después de todo, el mundo no tendría sentido si el operador == () y el operador! = () Devolvieran falso! Como tal, es común implementar estos operadores en términos unos de otros
Sin embargo, esto solo refleja el Common Sense ™, y no especifica que deban implementarse de esta manera.
Algunos antecedentes: solo estoy tratando de escribir una función que verifique si dos valores (de tipo desconocido) son iguales, e imprimir un mensaje de error si este no es el caso. Me gustaría decir que el concepto requerido aquí es que los tipos son EqualityComparable
. Pero para esto, uno todavía tendría que escribir if (!(x==y)) {...}
y no podría escribir if (x!=y) {...}
, porque esto usaría un operador diferente, que no está cubierto con el concepto de EqualityComparable
en absoluto, y que incluso podría estar sobrecargado de manera diferente ...
Actualizar
Sé que el programador básicamente puede hacer lo que quiera con sus sobrecargas personalizadas. Simplemente me pregunté si realmente se le permite hacer todo, o si hay normas impuestas por la norma. Tal vez una de estas afirmaciones sutiles que sugieren que desviarse de la implementación habitual causa un comportamiento indefinido, como el que NathanOliver mencionó en un comentario, pero que parecía referirse solo a ciertos tipos . Por ejemplo, el estándar establece explícitamente que para los tipos de contenedores , a!=b
es equivalente a !(a==b)
(sección 23.2.1, tabla 95, "Requisitos del contenedor").
Pero para los tipos generales, definidos por el usuario, actualmente parece que no existen tales requisitos. La pregunta está etiquetada language-lawyer
, porque esperaba una declaración / referencia definida, pero sé que esto puede ser casi imposible: aunque se podría señalar la sección donde se dice que los operadores deben ser negaciones entre sí, uno Difícilmente se puede demostrar que ninguna de las ~ 1500 páginas de la norma dice algo como esto ...
En caso de duda, y a menos que haya más sugerencias, votaré / aceptaré las respuestas correspondientes más adelante, y por ahora supondré que para comparar la no igualdad para EqualityComparable
tipos de igualdad de comparación se debe hacer con if (!(x==y))
para ser en el lado seguro.
En general, no creo que pueda confiar en ello, porque no siempre tiene sentido que el operator ==
y el operator!=
Correspondan siempre , por lo que no veo cómo el estándar podría exigirlo nunca.
Por ejemplo, considere los tipos de punto flotante incorporados, como los dobles, para los cuales los NaN siempre comparan falso, por lo que el operador == y el operador! = Pueden devolver falso al mismo tiempo. (Edición: Vaya, esto es incorrecto; consulta el comentario de hvd).
Como resultado, si estoy escribiendo una nueva clase con semántica de punto flotante (tal vez un really_long_double), tengo que implementar el mismo comportamiento para ser coherente con los tipos primitivos, por lo que mi operator==
tendría que comportarse igual y comparar dos NaN como falsos, aunque el operator!=
también los compara como falsos.
Esto podría surgir en otras circunstancias, también. Por ejemplo, si estaba escribiendo una clase para representar un valor nulable de la base de datos, podría encontrar el mismo problema, porque todas las comparaciones con la base de datos NULL son falsas. Podría elegir implementar esa lógica en mi código C ++ para tener la misma semántica que la base de datos.
En la práctica, sin embargo, para su caso de uso, puede que no valga la pena preocuparse por estos casos de borde. Solo documente que su función compara los objetos usando el operator== (or operator !=)
Y déjelo así.
No. Puede escribir sobrecargas de operadores para ==
y !=
Que hacen lo que usted desea. Probablemente sería una mala idea hacerlo, pero la definición de C ++ no obliga a esos operadores a ser los opuestos lógicos del otro.