Are = = and!= mutuamente dependiente?


Estoy aprendiendo sobre la sobrecarga de operadores en C++, y veo que == y != son simplemente algunas funciones especiales que se pueden personalizar para tipos definidos por el usuario. Mi preocupación es, sin embargo, ¿por qué se necesitan dos definiciones separadas? Pensé que si a == b es cierto, entonces a != b es automáticamente falsa, y viceversa, y no hay otra posibilidad, porque, por definición, a != b es !(a == b). Y no podía imaginar ninguna situación en la que esto no fuera cierto. Pero tal vez mi la imaginación es limitada o soy ignorante de algo?

Sé que puedo definir uno en términos del otro, pero esto no es lo que estoy preguntando. Tampoco estoy preguntando sobre la distinción entre comparar objetos por valor o por identidad. O si dos objetos podrían ser iguales y no iguales al mismo tiempo (¡esto definitivamente no es una opción! estas cosas son mutuamente excluyentes). Lo que estoy preguntando es esto:

¿Hay alguna situación posible en la que hacer preguntas sobre dos objetos siendo iguales tiene sentido, pero preguntar por ellos no siendo iguales no tiene sentido? (ya sea desde la perspectiva del usuario, o la perspectiva del implementador)

Si no existe tal posibilidad, entonces ¿por qué en la Tierra C++ tiene estos dos operadores siendo definidos como dos funciones distintas?

Author: Baum mit Augen, 2016-06-14

15 answers

Usted no quiere que el lenguaje reescriba automáticamente a != b como !(a == b) cuando a == b devuelve algo que no sea un bool. Y hay algunas razones por las que podrías hacer que lo haga.

Puede tener objetos expression builder, donde a == b no realiza ni pretende realizar ninguna comparación, sino que simplemente construye algún nodo de expresión que represente a == b.

Es posible que tenga una evaluación perezosa, donde a == b no realiza ni pretende realizar ninguna comparación directamente, pero en su lugar devuelve algún tipo de lazy<bool> que se puede convertir a bool implícita o explícitamente en algún momento posterior para realizar realmente la comparación. Posiblemente combinado con los objetos expression builder para permitir una optimización completa de la expresión antes de la evaluación.

Puede tener alguna clase de plantilla personalizada optional<T>, donde dadas las variables opcionales t y u, desea permitir t == u, pero haga que devuelva optional<bool>.

Probablemente hay más que no se me ocurrió. E incluso aunque en estos ejemplos la operación a == b y a != b tienen sentido, a != b no es lo mismo que !(a == b), por lo que se necesitan definiciones separadas.

 273
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 22:41:13

Si no existe tal posibilidad, entonces ¿por qué en la Tierra C++ tiene estos dos operadores siendo definidos como dos funciones distintas?

Porque puedes sobrecargarlos, y al sobrecargarlos puedes darles un significado totalmente diferente de su significado original.

Tomemos, por ejemplo, el operador <<, originalmente el operador de desplazamiento a la izquierda, ahora comúnmente sobrecargado como un operador de inserción, como en std::cout << something; significado totalmente diferente del original una.

Por lo tanto, si acepta que el significado de un operador cambia cuando lo sobrecarga, entonces no hay razón para evitar que el usuario le dé un significado a operador == que no sea exactamente la negación del operador !=, aunque esto podría ser confuso.

 110
Author: shrike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-14 15:34:00

Mi preocupación es, sin embargo, ¿por qué se necesitan dos definiciones separadas?

No tienes que definir ambos.
Si son mutuamente excluyentes, todavía puede ser conciso definiendo solo == y < junto con std:: rel_ops

Referencia:

#include <iostream>
#include <utility>

struct Foo {
    int n;
};

bool operator==(const Foo& lhs, const Foo& rhs)
{
    return lhs.n == rhs.n;
}

bool operator<(const Foo& lhs, const Foo& rhs)
{
    return lhs.n < rhs.n;
}

int main()
{
    Foo f1 = {1};
    Foo f2 = {2};
    using namespace std::rel_ops;

    //all work as you would expect
    std::cout << "not equal:     : " << (f1 != f2) << '\n';
    std::cout << "greater:       : " << (f1 > f2) << '\n';
    std::cout << "less equal:    : " << (f1 <= f2) << '\n';
    std::cout << "greater equal: : " << (f1 >= f2) << '\n';
}

¿Hay alguna situación posible en la que hacer preguntas sobre dos los objetos que son iguales tiene sentido, pero preguntar si no son iguales igual no hace sentido?

A menudo asociamos estos operadores a la igualdad.
Aunque así es como se comportan en los tipos fundamentales, no hay obligación de que este sea su comportamiento en los tipos de datos personalizados. Ni siquiera tienes que devolver un bool si no quieres.

He visto a personas sobrecargar operadores de formas extrañas, solo para encontrar que tiene sentido para su aplicación específica de dominio. Incluso si la interfaz parece mostrar que son mutuamente excluyentes, el autor puede querer añade lógica interna específica.

(ya sea desde la perspectiva del usuario, o desde la perspectiva del implementador)

Sé que quieres un ejemplo específico,
así que aquí hay uno del marco de pruebas de captura que pensé que era práctico:

template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) {
    return captureExpression<Internal::IsEqualTo>( rhs );
}

template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) {
    return captureExpression<Internal::IsNotEqualTo>( rhs );
}

Estos operadores están haciendo cosas diferentes, y no tendría sentido definir un método como una !(no) del otro. La razón por la que se hace esto, es para que el marco pueda imprimir la comparación realizada. En para hacer eso, necesita capturar el contexto de lo que se usó el operador sobrecargado.

 60
Author: Trevor Hickey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-14 02:46:12

Hay algunas convenciones muy bien establecidas en las que (a == b) y (a != b) son ambas falsas no necesariamente opuestas. En particular, en SQL, cualquier comparación con NULL produce NULL, not true o false.

Probablemente no sea una buena idea crear nuevos ejemplos de esto si es posible, porque es muy poco intuitivo, pero si está tratando de modelar una convención existente, es bueno tener la opción de hacer que sus operadores se comporten "correctamente" para ese contexto.

 42
Author: Jander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-16 04:09:30

Solo responderé la segunda parte de su pregunta, a saber:

Si no existe tal posibilidad, entonces ¿por qué en la Tierra C++ tiene estos dos operadores siendo definidos como dos funciones distintas?

Una razón por la que tiene sentido permitir que el desarrollador sobrecargue ambos es el rendimiento. Puede permitir optimizaciones implementando == y !=. Entonces x != y podría ser más barato que !(x == y) es. Algunos compiladores pueden ser capaces de optimizarlo para usted, pero tal vez no, especialmente si tienes objetos complejos con mucha ramificación involucrada.

Incluso en Haskell, donde los desarrolladores se toman muy en serio las leyes y los conceptos matemáticos, todavía se permite sobrecargar tanto == como /=, como se puede ver aquí ( http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html#v:-61--61-):

$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
λ> :i Eq
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
        -- Defined in `GHC.Classes'

Esto probablemente sería considerado micro-optimización, pero podría estar justificado para algunos casos.

 23
Author: Centril,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-14 23:11:00

¿Hay alguna situación posible en la que hacer preguntas sobre dos los objetos que son iguales tiene sentido, pero preguntar si no son iguales igual no tiene sentido? (ya sea desde la perspectiva del usuario, o perspectiva del implementador)

Esa es una opinión. Pero los diseñadores del lenguaje, al no ser omniscientes, decidieron no restringir a las personas que podrían llegar a situaciones en las que podría tener sentido (al menos para ellos).

 16
Author: Benjamin Lindley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 22:30:22

En respuesta a la edición;

Es decir, si es posible que algún tipo tenga el operador == pero no el !=, o viceversa, y cuándo tiene sentido hacerlo.

En general, no, no tiene sentido. Los operadores de igualdad y relacionales generalmente vienen en conjuntos. Si existe la igualdad, entonces la desigualdad también; menor que, entonces mayor que y así sucesivamente con el <= etc. Un enfoque similar se aplica a los operadores aritméticos también, también generalmente vienen en conjuntos lógicos naturales.

, Esto se evidencia en el std::rel_ops espacio de nombres. Si implementa los operadores equality y less than, el uso de ese espacio de nombres le da los otros, implementados en términos de sus operadores implementados originales.

Dicho todo, ¿hay condiciones o situaciones en las que uno no significaría inmediatamente el otro, o no podría implementarse en términos de los otros? Sí hay, posiblemente pocos, pero son allí; de nuevo, como se evidencia en el rel_ops siendo un espacio de nombres propio. Por esa razón, permitir que se implementen de forma independiente le permite aprovechar el lenguaje para obtener la semántica que necesita o necesita de una manera que sigue siendo natural e intuitiva para el usuario o cliente del código.

La evaluación perezosa ya mencionada es un excelente ejemplo de esto. Otro buen ejemplo es darles semántica que no significa igualdad o in-igualdad en absoluto. Un ejemplo similar a esto es los operadores de desplazamiento de bits << y >> se utilizan para la inserción y extracción de flujos. Aunque puede ser mal visto en los círculos generales, en algunas áreas específicas de dominio puede tener sentido.

 13
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-15 11:15:17

Si los operadores == y != no implican igualdad, de la misma manera que los operadores de flujo << y >> no implican desplazamiento de bits. Si tratas los símbolos como si significaran algún otro concepto, no tienen que ser mutuamente excluyentes.

En términos de igualdad, podría tener sentido si su caso de uso garantiza el tratamiento de objetos como no comparables, de modo que cada comparación debe devolver false (o un tipo de resultado no comparable, si sus operadores devuelven no bool). No puedo piense en una situación específica en la que esto estaría justificado, pero podría ver que es lo suficientemente razonable.

 12
Author: Taywee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-20 16:32:57

Con gran poder viene gran responsablemente, o al menos muy buenas guías de estilo.

== y != puede estar sobrecargado para hacer lo que quieras. Es una bendición y una maldición. No hay garantía de que != significa !(a==b).

 7
Author: It'sPete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-03 01:13:03
enum BoolPlus {
    kFalse = 0,
    kTrue = 1,
    kFileNotFound = -1
}

BoolPlus operator==(File& other);
BoolPlus operator!=(File& other);

No puedo justificar la sobrecarga de este operador, pero en el ejemplo anterior es imposible definir operator!= como el "opuesto" de operator==.

 6
Author: Dafang Cao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 22:44:27

Al final, lo que está comprobando con esos operadores es que la expresión a == b o a != b está devolviendo un valor booleano (true o false). Esta expresión devuelve un valor Booleano después de la comparación en lugar de ser mutuamente excluyentes.

 5
Author: Anirudh Sohil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 09:47:09

[..] ¿por qué se necesitan dos definiciones separadas?

Una cosa a considerar es que podría haber la posibilidad de implementar uno de estos operadores de manera más eficiente que simplemente usar la negación del otro.

(Mi ejemplo aquí fue una basura, pero el punto sigue en pie, piense en los filtros bloom, por ejemplo: Permiten probar rápidamente si algo está no en un conjunto, pero probar si está dentro puede tomar mucho más tiempo.)

[..] por definición, a != b es !(a == b).

Y es tu responsabilidad como programador hacer eso. Probablemente una buena cosa para escribir un examen.

 4
Author: Daniel Jour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-13 22:50:12

Al personalizar el comportamiento de los operadores, puede hacer que hagan lo que quiera.

Es posible que desee personalizar las cosas. Por ejemplo, es posible que desee personalizar una clase. Los objetos de esta clase se pueden comparar simplemente marcando una propiedad específica. Sabiendo que este es el caso, puede escribir algún código específico que solo verifique las cosas mínimas, en lugar de verificar cada bit de cada propiedad en todo el objeto.

Imagine un caso donde pueda averiguar que algo es diferente igual de rápido, si no más rápido, de lo que puedes descubrir que algo es lo mismo. Por supuesto, una vez que averigües si algo es igual o diferente, entonces puedes saber lo contrario simplemente volteando un poco. Sin embargo, voltear ese bit es una operación adicional. En algunos casos, cuando el código se vuelve a ejecutar mucho, guardar una operación (multiplicada por muchas veces) puede tener un aumento de velocidad general. (Por ejemplo, si guarda una operación por píxel de una pantalla de megapíxeles, entonces acabas de salvar un millón de operaciones. Multiplicado por 60 pantallas por segundo, y usted ahorra aún más operaciones.)

La respuesta de Hvd proporciona algunos ejemplos adicionales.

 2
Author: TOOGAM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-18 00:20:42

Sí, porque uno significa "equivalente" y otro significa "no equivalente" y estos términos son mutuamente excluyentes. Cualquier otro significado para estos operadores es confuso y debe evitarse por todos los medios.

 2
Author: oliora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-29 23:01:48

Tal vez una regla incomparable, donde a != b era false y a == b era false como un bit sin estado.

if( !(a == b || a != b) ){
    // Stateless
}
 2
Author: ToñitoG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 09:46:22