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:
-
No tiene sentido que cada tipo sea comparado con el
operator<
, por lo que el tipo no necesariamente define unoperator<
.-
Esto significa que el
operator==
no se puede definir automáticamente en términos deoperator<
-
Esto significa que el
-
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 aa == b
puede no ser necesariamente cierta, suponiendo que esos operadores estén definidos.
-
Esto significa que su afirmación sobre
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";
}
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.