No == operator found when comparing structures in C ++

Comparing two instances of the following structure, I get an error message:

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; }; 

Mistake:

error C2678: binary '==': no ​​operator found that accepts a left operand of type 'myproj :: MyStruct1' (or there is not an acceptable conversion)

Why?

+82
c ++ struct comparison-operators
Apr 21 '11 at 6:21
source share
8 answers

In C ++, struct do not have a comparison operator generated by default. You must write your own:

 bool operator==(const MyStruct1& lhs, const MyStruct1& rhs) { return /* your comparison code goes here */ } 
+112
Apr 21 '11 at 6:25
source share

As other people have said, you need to implement the comparison function yourself.

There is a suggested way to ask the compiler to generate an obvious / naive (?) Implementation: see here .

C ++ may seem a little useless to not standardize this, but often structures / classes have some data elements that need to be excluded from comparison (for example, counters, cached results, container capacity, last successful operation code / error code, cursors), as well decisions on a variety of things, including but not limited to:

  • which fields to compare first, for example, comparing a specific int member can very quickly eliminate 99% of unequal objects, while the map<string,string> element can often have the same records and it is comparatively expensive to compare - if the values ​​are loaded at run time, the programmer can understand that the compiler cannot
  • string comparison: case sensitivity, equivalence of spaces and delimiters, withdrawal from conventions ...
  • precision when comparing floating point numbers
  • whether NaN floating point values ​​should be considered equal
  • comparing pointers or pointers to data (and, if the latter, how to find out if pointers belong to arrays and how many objects / bytes need to be compared)
  • Does the order matter when comparing unsorted containers (for example, vector , list ) and, if so, is it possible to sort them in place before comparing or using additional memory to sort the temporary data each time the comparison is performed
  • how many elements of the array currently contain valid values ​​that should be compared (is there a size or an hour somewhere?)
  • which union member to compare
  • normalization: for example, date types can allow a day-off or month-year off, or a rational / fractional object can have 6/8, and the other has 3/4, which, for performance reasons, they adjust lazily with a separate normalization step; You may need to decide whether to initiate normalization before comparing.
  • what to do if weak pointers are invalid
  • how to work with members and databases that themselves do not implement operator== (but can have compare() or operator< or str() or getters ...)
  • what locks should be performed when reading / comparing data that other threads might want to update

Thus, it is nice to have a mistake until you clearly think about what the comparison should mean for your particular structure, instead of allowing it to compile, but not give a meaningful result at runtime .

All this suggests that it would be nice if C ++ allowed you to say bool operator==() const = default; when you decided that the "naive" test "member by member" == was in order. The same for != Given multiple members / databases, the implementation of "default" < , <= , > and >= seems hopeless, although cascading based on the declaration order is possible, but very unlikely to be required given the conflicting imperatives for ordering members ( the grounds are mandatory before the members, grouping by accessibility, construction / destruction before dependent use). To be more widely useful, C ++ will require a new annotation system for data / database elements that will guide the selection - it would be nice to have in the Standard, albeit ideally in combination with generating custom code based on AST ... I expect that it will happen one day.

Typical implementation of equality operators

Believable implementation

It is likely that a reasonable and effective implementation would be:

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

Please note that operator== also needed for MyStruct2 .

The implications of this implementation and its alternatives are discussed in the "Discussion of Your MyStruct1 Features" section below.

A consistent approach to ==, <,> <=, etc.

It's easy to use std::tuple comparison operators to compare your own class instances - just use std::tie to create tuples of field references in the desired comparison order. Summarizing my example from 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... 

When you “own” (that is, you can edit the factor with corporate and third-party libraries) the class you want to compare, and especially with the readiness of C ++ 14 to deduce the type of the return value of the function from the return , it is often better to add a “bind” member function to the class, which you want to compare:

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

Then the above comparisons simplify to:

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

If you need a more complete set of comparison operators, I suggest increasing the operators (look for less_than_comparable ). If for some reason this does not fit, you may or may not like the idea of ​​supporting macros (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) 

... what can be used a la ...

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

(C ++ 14-member version here )

Discussion of the specifics of your MyStruct1

There are consequences for choosing to provide a standalone operator==() member of operator==() ...

Standalone implementation

You need to make an interesting decision. Since your class can be implicitly created from MyStruct2 , a standalone / non bool operator==(const MyStruct2& lhs, const MyStruct2& rhs) member bool operator==(const MyStruct2& lhs, const MyStruct2& rhs) function will support ...

 my_MyStruct2 == my_MyStruct1 

... first creating a temporary MyStruct1 from my_myStruct2 , and then doing a comparison. This will definitely leave MyStruct1::an_int set to the default constructor parameter -1 . Depending on whether you an_int comparison in the implementation of your operator== , MyStruct1 may or may not compare it with MyStruct2 which itself is compared to MyStruct1 my_struct_2 ! In addition, creating a temporary MyStruct1 can be a very inefficient operation, as it involves copying the existing member my_struct2 to a temporary one, only to drop it after comparison. (Of course, you can prevent this implicit MyStruct1 construct for comparison by MyStruct1 making this constructor explicit or by removing the default value for an_int .)

Implementation of the participants

If you want to avoid implicitly constructing MyStruct1 from MyStruct2 , make the comparison operator a member function:

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

Note that the const keyword - necessary only for member implementation - tells the compiler that the objects being compared do not change them, therefore they can be allowed for const objects.

Comparing Visible Views

Sometimes the easiest way to get the comparison you want may be ...

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

... which is often very expensive - these string painfully created to be thrown away! For types with floating point values, comparing visible representations means that the number of digits displayed determines the tolerance within which almost equal values ​​are considered equal during comparison.

+76
Apr 21 2018-11-11T00:
source share

You need to explicitly specify operator == for MyStruct1 .

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

Now comparison == is legal for 2 such objects.

+10
Apr 21 '11 at 6:25
source share

Comparison does not work on structures in C or C ++. Compare by fields.

+2
Apr 21 '11 at 6:25
source share

By default, structures do not have an == operator. You will have to write your own implementation:

 bool MyStruct1::operator==(const MyStruct1 &other) const { ... // Compare the values, and return a bool result. } 
+1
Apr 21
source share

Out of the box, the == operator only works for primitives. To make your code work, you need to overload the == operator for your structure.

0
Apr 21 '11 at 6:25
source share

Because you are not writing a comparison operator for your structure. The compiler does not generate it for you, so if you want to compare, you must write it yourself.

0
Apr 21 '11 at 6:25
source share

Starting with C ++ 20, it should be possible to add a full set of default comparison operators ( == , <= , etc.) to the class by declaring a default tripartite comparison operator (the spaceship operator ), for example this one:

 struct Point { int x; int y; auto operator<=>(const Point&) const = default; }; 

If you have a compatible C ++ 20 compiler, adding this line to MyStruct1 and MyStruct2 may be sufficient for equality matching, provided that the definition of MyStruct2 is compatible.

0
Mar 27 '19 at 20:35
source share



All Articles