overload operator c++ operator-overloading

c++ - operator - ¿Podría usar el operador== si solo implementara el operador<?



operator[] c++ (13)

Lógicamente, si! (A <b) y! (B <a) significa a == b. ¿C ++ infiere esto automáticamente? ¿Puedo usar == si solo implementé

Para poner lo que otros han dicho en términos matemáticos: suponiendo que tiene un operator < que devuelve bool y define un orden débil estricto , e implementa el operator == como retornando !(a < b) && !(b < a) , entonces Este operador define una relación de equivalencia consistente con el orden débil estricto dado. Sin embargo, C ++ no requiere que el operator < defina un orden débil estricto, ni el operator == para definir una relación de equivalencia (aunque muchos algoritmos estándar como sort pueden usar implícitamente estos operadores y requieren una relación de equivalencia rsp. De orden débil estricto).

Si desea definir todos los demás operadores relacionales basados ​​en y de acuerdo con el estricto orden débil de su operator < , Boost.Operators puede ahorrarle algo de tipeo.

Debido a que es muy fácil hacer un mal uso de un operator < que no cumple con los requisitos del algoritmo estándar, por ejemplo, al usarlo accidentalmente a través de std::sort , std::lower_bound , etc., recomiendo definir el operator < como un orden débil estricto o no en absoluto. El ejemplo que dio CodesInChaos es un orden parcial, que no cumple con el requisito de "transitividad de incomparabilidad" de un orden débil estricto. Por lo tanto, recomendaría llamar a dicha relación con un nombre diferente, por ejemplo, bool setLess(const MySet &, const MySet &) .

Fuentes:

He implementado el operator< para cierto objeto. Lógicamente, si !(a < b) y !(b < a) significa a == b .

¿Se infiere esto automáticamente? ¿Puedo usar == si solo implemento < ?


Además de otras respuestas,

¡El compilador ni siquiera puede inferir != ==

struct MyType { int value; }; bool operator == (const MyType& a, const MyType& b) { return a.value == b.value; } int main() { MyType a = {3}; MyType b = {4}; if (a != b) // (* compilation Error *) std::cout << "a does not equal b" << std::endl; }

Sin embargo, sería bueno si hay una opción para decirle al compilador que el resto de los operadores racionales se aplican a su clase.

Hay, como se explica en algunas respuestas en el encabezado <utility> algo que puede proporcionar dicha funcionalidad. necesitará agregar la siguiente línea al comienzo de la página main :

using namespace std::rel_ops;

Sin embargo, el uso de este enfoque es costoso y causará ambigüedades de sobrecarga en todo el lugar, como lo señaló JDługosz.


C ++ no infiere esto automáticamente. Para el operator> , operator<= y operator>= , puede usar std::rel_ops ; esto requiere solo operator< . Sin embargo, no proporciona operator== en términos de operator< . Puedes hacerlo tú mismo así:

template <class T> bool operator==(T const& lhs, T const& rhs) { return !((lhs < rhs) or (rhs < lhs)); }

Tenga en cuenta que !((lhs < rhs) or (rhs < lhs)) y !(lhs < rhs) and !(rhs < lhs) son equivalentes, matemáticamente.


C ++ no puede inferir esto automáticamente por un par de razones:

  1. No tiene sentido que cada tipo sea comparado con el operator< , por lo que el tipo no necesariamente define un operator< .
    • Esto significa que el operator== no se puede definir automáticamente en términos de operator<
  2. operator< no es necesario para comparar sus argumentos. Un programador puede definir operadores para que sus tipos hagan casi cualquier cosa con sus argumentos.
    • Esto significa que su afirmación sobre !(a < b) && !(b < a) es equivalente a a == b puede no ser necesariamente cierta, suponiendo que esos operadores estén definidos.

Si desea una función de operator== para sus tipos, solo defina una. No es tan dificil :)

// For comparing, something like this is used bool operator==(const MyType& lhs, const MyType& rhs) { // compare (or do other things!) however you want } // ... though it''s not the only thing you can do // - The return type can be customised // - ... as can both of the arguments const MyType& operator==(int* lhs, const MyType* const rhs) { return lhs; }


Como muchos han dicho, no, no puedes, y no, el compilador no debería.

Esto no significa que no deba ser fácil pasar de un < a == y toda la miríada.

boost::operators intenta facilitarlo. Úselo y listo.

Si desea hacerlo usted mismo, solo se necesita un poco de código para reimplementar lo que le brinda el impulso:

namespace utility { namespace details { template<class...>using void_t=void; template<template<class...>class Z, class, class...Ts> struct can_apply:std::false_type{}; template<template<class...>class Z, class...Ts> struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{}; } template<template<class...>class Z, class...Ts> using can_apply = ::utility::details::can_apply<Z,void,Ts...>; } namespace auto_operators { template<class T, class U> using less_r = decltype( std::declval<T const&>() < std::declval<U const&>() ); template<class T, class U> using can_less = ::utility::can_apply<less_r, T, U>; struct order_from_less { template<class T, class U> using enabled = std::enable_if_t< std::is_base_of<order_from_less, T>{} && std::is_base_of<order_from_less, U>{} && can_less<T, U>{}, bool >; template<class T, class U> friend enabled<U,T> operator>(T const& lhs, U const& rhs) { return rhs < lhs; } template<class T, class U> friend enabled<U,T> operator<=(T const& lhs, U const& rhs) { return !(lhs > rhs); } template<class T, class U> friend enabled<T,U> operator>=(T const& lhs, U const& rhs) { return !(lhs < rhs); } }; struct equal_from_less:order_from_less { template<class T, class U> using enabled = std::enable_if_t< std::is_base_of<order_from_less, T>{} && std::is_base_of<order_from_less, U>{} && can_less<T, U>{} && can_less<U,T>{}, bool >; template<class T, class U> friend enabled<U,T> operator==(T const& lhs, U const& rhs) { return !(lhs < rhs) && !(rhs < lhs); } template<class T, class U> friend enabled<U,T> operator!=(T const& lhs, U const& rhs) { return !(lhs==rhs); } }; }

Lo anterior solo tiene que escribirse una vez, o cose equivalente obtenido de #include boost.

Una vez que tienes impulso, o lo anterior, es tan simple como algo como:

struct foo : auto_operators::equal_from_less { int x; foo( int in ):x(in) {} friend bool operator<( foo const& lhs, foo const& rhs ) { return lhs.x < rhs.x; } };

y foo ahora tiene todos los operadores de pedidos y comparación definidos en él.

int main() { foo one{1}, two{2}; std::cout << (one < two) << "/n"; std::cout << (one > two) << "/n"; std::cout << (one == two) << "/n"; std::cout << (one != two) << "/n"; std::cout << (one <= two) << "/n"; std::cout << (one >= two) << "/n"; std::cout << (one == one) << "/n"; std::cout << (one != one) << "/n"; std::cout << (one <= one) << "/n"; std::cout << (one >= one) << "/n"; }

Ejemplo en vivo .

El punto de todo esto es que C ++ no asume, como lenguaje, que < significa > y >= y == todos tienen sentido. Pero puede escribir una biblioteca que le permita tomar un tipo con < definido, y agregar una clase base trivial de repente hace que todas esas otras operaciones se definan con un costo de tiempo de ejecución cero.


Considere el siguiente ejemplo:

class point{ unsigned int x; unsigned int y; public: bool operator <(const point& other){ return (x+y) < (other.x+other.y); } bool operator == (const point& other){ return (x==other.x) && (y==other.y); } }

Y luego tenemos:

point a{1, 2}; point b{2, 1};

! (a <b),! (b <a), pero también! (a == b).


El compilador no infiere == de < .

Puede verificar eso con un simple ejemplo:

#include <iostream> struct A { A(int r):i{r}{} int i; }; bool operator<(A const & a1, A const& a2) { return a1.i < a2.i; } int main(int argc, char* argv[]) { A a1{2}; A a2{3}; if(a1 == a2) { std::cout << "equals/n"; } return 0; }

GCC te da este error:

main.cpp:20:11: error: no match for ''operator=='' (operand types are ''A'' and ''A'') if(a1 == a2) {


Hay plantillas definidas en el std::rel_ops nombres std::rel_ops que son operadores que faltan y definen automáticamente.

No define un operador de igualdad basado en el operador menos como desee.

Aún así, esto es bastante útil; si define el operador menos y el operador de igualdad, tendrá los otros operadores de comparación de forma gratuita.


La respuesta es NO, solo necesitas una prueba simple

struct MyType{ int value; }; bool operator < (MyType& a, MyType& b) { return a.value < b.value; } int main(int argc, char* argv[]) { MyType a = {3}; MyType b = {4}; if (a == b) std::cout << "a==b" << std::endl; if (a < b) std::cout << "a < b" << std::endl; }

g ++ 4.8.2 se queja:

main.cpp: en la función ''int main (int, char **)'':

main.cpp: 16: 11: error: no coincide con ''operator =='' (los tipos de operando son ''MyType'' y ''MyType'')

Pero hay algo similar que funciona en C ++, consulte estos http://en.cppreference.com/w/cpp/concept/Compare

dice:

equiv (a, b), una expresión equivalente a! comp (a, b) &&! comp (b, a)


La respuesta es clara NO. No hay forma implícita. Las clases C ++ permiten que los operadores se sobrecarguen. Entonces, su idea es que lógicamente, if !(a < b) and !(b < a) significa a == b . es correcto. Y puede sobrecargar a los operadores como se muestra a continuación. Por ejemplo, una clase de fracción:

class Fraction { int num; int denom; . . . public: . . . bool operator < (const Fraction &other) { if ((this->num * other.denom) < (this->denom * other.num)) return false; else return true; } bool operator == (const Fraction &other) ( if (!(*this < other) && !(other < *this)) { return true; else return false; } };


Mas o menos.
Pero necesitarías un boost::operators

Los operadores sobrecargados para los tipos de clase generalmente ocurren en grupos. Si puedes escribir x + y, probablemente también quieras poder escribir x + = y. Si puedes escribir x <y, también quieres x> y, x> = y, y x <= y. Además, a menos que su clase tenga un comportamiento realmente sorprendente, algunos de estos operadores relacionados pueden definirse en términos de otros (por ejemplo, x> = y <=>! (X <y)). La replicación de esta plantilla para múltiples clases es tediosa y propensa a errores. Las plantillas boost / operadores.hpp ayudan al generar operadores para usted en el ámbito del espacio de nombres en función de otros operadores que haya definido en su clase.


No puede inferir == de < porque no todos los tipos están ordenados, como std::complex . ¿Es 2 + 3i > 1 + 4i o no?

Además, incluso en los tipos que normalmente se ordenan, aún no se puede inferir la igualdad de > o < , por ejemplo, IEEE-754 NaN

double n = std::numeric_limits<double>::quiet_NaN(); std::cout << "NaN == NaN: " << (n == n) << ''/n''; std::cout << "NaN < NaN: " << (n < n) << ''/n''; std::cout << "NaN > NaN: " << (n > n) << ''/n''; std::cout << "NaN != NaN: " << (n != n) << ''/n'';

Todos volverán falsos excepto el último


No. Este método funciona bien en objetos con números que se llaman totalmente ordenados . Para todo tipo de conjunto / clase, nadie puede garantizar esta relación. Incluso nadie puede garantizar que un operator < compare algo.

Entonces == no es más que == . Puede implementar == por < pero esto no funciona para todos y los estándares C ++ no lo harán por usted.