punteros lenguaje funciones estructuras ejercicios declaracion con arreglo apuntadores anidadas c++ struct comparison-operators

c++ - lenguaje - funciones con estructuras en c



No== operador encontrado al comparar estructuras en C++ (7)

Como han dicho otras personas, debe implementar una función de comparación usted mismo.

Hay una forma propuesta de pedirle al compilador que genere la implementación obvia / ingenua (?): Mira here .

Puede parecer poco útil que C ++ no haya estandarizado esto, pero a menudo las estructuras / clases tienen algunos miembros de datos para excluir de la comparación (por ejemplo, contadores, resultados en caché, capacidad del contenedor, código de éxito / error de la última operación, cursores). como decisiones para hacer sobre miles de cosas que incluyen pero no se limitan a:

  • qué campos comparar primero, por ejemplo, comparar un miembro int particular podría eliminar el 99% de objetos desiguales muy rápidamente, mientras que un miembro map<string,string> menudo podría tener entradas idénticas y ser relativamente costoso de comparar, si los valores se cargan en tiempo de ejecución , el programador puede tener ideas que el compilador no puede
  • al comparar cadenas: sensibilidad de caso, equivalencia de espacios en blanco y separadores, convenciones de escape ...
  • precisión al comparar flotadores / dobles
  • si los valores de coma flotante NaN deben considerarse iguales
  • comparar punteros o apuntado a datos (y si es este último, cómo saber si los punteros son para matrices y cuántos objetos / bytes necesitan comparación)
  • si el orden es importante cuando se comparan contenedores no ordenados (por ejemplo, vector , list ), y si es correcto ordenarlos en el lugar antes de comparar versus usar memoria extra para ordenar los temporales cada vez que se realiza una comparación
  • ¿Cuántos elementos de matriz actualmente tienen valores válidos que se deben comparar (hay un tamaño en algún lugar o un centinela?)
  • qué miembro de un union comparar
  • normalización: por ejemplo, los tipos de fecha pueden permitir un día fuera de rango o un mes del año, o un objeto racional / fracción puede tener 6/8 mientras que otro tiene 3/4, que por razones de rendimiento corrigen perezosamente con un paso de normalización separado; es posible que deba decidir si activa una normalización antes de la comparación
  • qué hacer cuando los punteros débiles no son válidos
  • cómo manejar miembros y bases que no implementan operator== ellos mismos (pero podrían tener compare() o operator< o str() o getters ...)
  • qué bloqueos deben tomarse al leer / comparar datos que otros hilos pueden querer actualizar

Por lo tanto, es agradable tener un error hasta que haya pensado explícitamente sobre qué debe significar la comparación para su estructura específica, en lugar de dejar que se compile, pero no darle un resultado significativo en el tiempo de ejecución .

Dicho todo esto, sería bueno si C ++ te dejara decir bool operator==() const = default; cuando decidiste que una prueba "ingenua" de miembro por miembro == estaba bien. Lo mismo para != . Sin embargo, dada la multiplicidad de miembros / bases, las implementaciones "predeterminadas" < , <= , > y >= parecen inútiles: es posible establecer una cascada sobre la base del orden de la declaración, pero es poco probable que sea lo que se busca, teniendo en cuenta los imperativos conflictivos necesariamente antes de los miembros, agrupando por accesibilidad, construcción / destrucción antes del uso dependiente). Para ser más útil, C ++ necesitaría un nuevo sistema de anotación de miembro / base de datos para guiar las elecciones; sin embargo, eso sería algo genial en el estándar, idealmente emparejado con la generación de código definido por el usuario basado en AST ... espero sucederá un día.

Implementación típica de operadores de igualdad

Una implementación plausible

Es probable que una implementación razonable y eficiente sea:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs) { return lhs.my_struct2 == rhs.my_struct2 && lhs.an_int == rhs.an_int; }

Tenga en cuenta que esto también necesita un operator== para MyStruct2 .

Las implicaciones de esta implementación y las alternativas se tratan bajo el encabezado Discusión de detalles de su MyStruct1 a continuación.

Un enfoque consistente para ==, <,> <= etc.

Es fácil aprovechar los operadores de comparación de std::tuple para comparar sus propias instancias de clase; solo use std::tie para crear tuplas de referencias a campos en el orden de comparación deseado. Generalizando mi ejemplo desde here :

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs) { return std::tie(lhs.my_struct2, lhs.an_int) == std::tie(rhs.my_struct2, rhs.an_int); } inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs) { return std::tie(lhs.my_struct2, lhs.an_int) < std::tie(rhs.my_struct2, rhs.an_int); } // ...etc...

Cuando "posee" (es decir, puede editar, un factor con bibliotecas corporativas y de terceros) la clase que desea comparar, y especialmente con la preparación de C ++ 14 para deducir el tipo de retorno de función de la declaración de return , a menudo es más agradable agregar un función de miembro "atar" a la clase que desea poder comparar:

auto tie() const { return std::tie(my_struct1, an_int); }

Entonces, las comparaciones anteriores simplifican a:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs) { return lhs.tie() == rhs.tie(); }

Si quiere un conjunto más completo de operadores de comparación, sugiero que aumenten los operadores (busque less_than_comparable ). Si no es adecuado por alguna razón, puede o no gustarle la idea de las macros de soporte (online) :

#define TIED_OP(STRUCT, OP, GET_FIELDS) / inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) / { / return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); / } #define TIED_COMPARISONS(STRUCT, GET_FIELDS) / TIED_OP(STRUCT, ==, GET_FIELDS) / TIED_OP(STRUCT, !=, GET_FIELDS) / TIED_OP(STRUCT, <, GET_FIELDS) / TIED_OP(STRUCT, <=, GET_FIELDS) / TIED_OP(STRUCT, >=, GET_FIELDS) / TIED_OP(STRUCT, >, GET_FIELDS)

... que luego puede usarse a la ...

#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)

(Versión de lazo de miembro C ++ 14 here )

Discusión de detalles de su MyStruct1

Hay implicaciones para la elección de proporcionar un operator==() autónomo versus miembro operator==() ...

Implementación independiente

Tienes una decisión interesante que tomar. Como su clase se puede construir implícitamente a partir de un MyStruct2 , una función de bool operator==(const MyStruct2& lhs, const MyStruct2& rhs) / no miembro bool operator==(const MyStruct2& lhs, const MyStruct2& rhs) ...

my_MyStruct2 == my_MyStruct1

... creando primero un MyStruct1 temporal de my_myStruct2 , luego haciendo la comparación. Esto definitivamente dejaría MyStruct1::an_int establecido en el valor del parámetro predeterminado del constructor de -1 . Dependiendo de si incluye an_int comparación an_int en la implementación de su operator== , un MyStruct1 podría o no ser igual a un MyStruct2 que se compare con el miembro my_struct_2 . Además, crear un MyStruct1 temporal puede ser una operación muy ineficiente, ya que implica copiar el miembro my_struct2 existente en un temporal, solo para tirarlo después de la comparación. (Por supuesto, podría evitar esta construcción implícita de MyStruct1 s para comparar haciendo que ese constructor sea explicit o elimine el valor predeterminado para an_int ).

Implementación del miembro

Si desea evitar la construcción implícita de un MyStruct1 desde un MyStruct2 , haga que el operador de comparación sea una función miembro:

struct MyStruct1 { ... bool operator==(const MyStruct1& rhs) const { return tie() == rhs.tie(); // or another approach as above } };

Tenga en cuenta que la palabra clave const (solo necesaria para la implementación del miembro) le informa al compilador que la comparación de objetos no los modifica, por lo que puede permitirse en objetos const .

Comparando las representaciones visibles

A veces, la forma más fácil de obtener el tipo de comparación que desea puede ser ...

return lhs.to_string() == rhs.to_string();

... que a menudo también es muy caro, ¡esas string dolorosamente creadas para ser tiradas! Para los tipos con valores de coma flotante, la comparación de las representaciones visibles significa que el número de dígitos que se muestran determina la tolerancia dentro de la cual los valores casi iguales se tratan como iguales durante la comparación.

Comparando dos instancias de la siguiente estructura, recibo un error:

struct MyStruct1 { Position(const MyStruct2 &_my_struct_2, const int _an_int = -1) : my_struct_2(_my_struct_2), an_int(_an_int) {} std::string toString() const; MyStruct2 my_struct_2; int an_int; };

El error es:

error C2678: binario ''=='': no ​​se ha encontrado ningún operador que tome un operando de la izquierda del tipo ''myproj :: MyStruct1'' (o no hay una conversión aceptable)

¿Por qué?


En C ++, las struct no tienen un operador de comparación generado por defecto. Necesitas escribir el tuyo:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs) { return /* your comparison code goes here */ }


Fuera de la caja, el operador == solo funciona para primitivos. Para que su código funcione, necesita sobrecargar el operador == para su estructura.


La comparación no funciona en estructuras en C o C ++. Compare por campos en su lugar.


Necesita definir explícitamente operator == para MyStruct1 .

struct MyStruct1 { bool operator == (const MyStruct1 &rhs) const { /* your logic for comparision between "*this" and "rhs" */ } };

Ahora la comparación == es legal para 2 de tales objetos.


Por defecto las estructuras no tienen un operador == . Deberá escribir su propia implementación:

bool MyStruct1::operator==(const MyStruct1 &other) const { ... // Compare the values, and return a bool result. }


Porque no escribiste un operador de comparación para tu estructura. El compilador no lo genera para usted, por lo que si desea una comparación, debe escribirlo usted mismo.