c++ - libreria - ¿Es seguro usar operator!=As operator<en un estándar:: set?
sort algorithm c++ (4)
Tengo una estructura con algunos miembros y tengo un operador implementado == para ello. ¿Es seguro implementar el operador <con la ayuda del operador ==? Quiero usar esta estructura en un conjunto, y quiero verificar que esta estructura sea única.
struct Data
{
std::string str1;
std::string str2;
std::string str3;
std::string str4;
bool operator==(const Data& rhs)
{
if (str1 == rhs.str1
&& str2 == rhs.str2
&& str3 == rhs.str3
&& str4 == rhs.str4
)
return true;
else
return false;
}
// Is this ok??
bool operator<(const Data& rhs)
{
return !this->operator==(rhs);
}
}
Entonces, cuando inserto esta estructura en un estándar, ¿qué pasará?
Bueno, tu código sugiere que si A!=B
, eso significa que A<B
es definitivamente incorrecto, ya que también puede ser A>B
Tendrá que implementar sus operadores >
y <
la misma manera que lo hizo con el operator==
, lo que significa al comparar los objetos en forma de miembro. Depende de usted decidir cómo determinar si A
es "más" o "menos" que B
según sus miembros.
Si usa el operador como lo tiene en cualquiera de los contenedores de biblioteca estándar, obtendrá UB.
Necesita definir operator<
en sus propios términos. No puede implementar operator<
en términos de operator==
, aunque puede hacer lo contrario.
Considera la paradoja en esta tabla de verdad:
"a" < "b" : TRUE
"b" < "a" : TRUE
Si su implementación de operator<
produce la paradoja anterior, que lo hace si la implementa en términos de operator==
entonces no ha implementado correctamente un orden débil estricto. Lo que has implementado es un desastre.
Debe determinar cuál de las cadenas miembro tiene prioridad sobre las demás, y luego realizar una comparación entre ellas en orden, de mayor a menor importancia.
Por ejemplo, si la precedencia de la cadena es, de lo más importante a lo menos importante:
-
str1
-
str2
-
str3
-
str4
... entonces esto produce el siguiente algoritmo para el operator<
:
bool operator<(const Data& rhs) const
{
if( str1 < rhs.str1 )
return true;
if( rhs.str1 < str1 )
return false;
if( str2 < rhs.str2 )
return true;
if( rhs.str2 < str2 )
return false;
if( str3 < rhs.str3 )
return true;
if( rhs.str3 < str3 )
return false;
if( str4 < rhs.str4 )
return true;
if( rhs.str4 < str4 )
return false;
return false;
}
Utilizando esto, podría opcionalmente volver a implementar operator==
en términos de operator<
. Sin embargo, usted asume la ineficiencia inherente de la complejidad del tiempo al hacerlo:
bool operator==(const Data& rhs) const
{
return !operator<(rhs) && !rhs.operator<(*this);
}
No, es bastante inseguro. La forma más sencilla de implementarlo es a través de std::tie
.
#include <tuple>
struct Data
{
std::string str1;
std::string str2;
std::string str3;
std::string str4;
bool operator<(const Data& rhs) const // you forgot a const
{
return
std::tie(str1, str2, str3, str4) <
std::tie(rhs.str1, rhs.str2, rhs.str3, rhs.str4);
}
}
No, eso no es seguro. La forma en que ha definido <
, a < b
y b < a
serán ambas verdaderas al mismo tiempo.
Entonces, cuando inserto esta estructura en un estándar, ¿qué pasará?
El comportamiento no está definido, por lo que todo está permitido, y es probable que sea diferente en diferentes implementaciones.