Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement C++ (in)equality operators for aggregate structs?

Sometimes I have structs such as this --

struct aggregate1 {
  std::string name;
  std::vector<ValueT> options;
  size_t foobar;
  // ...
};

-- where (in)equality is simply defined as (in)equality of all members: lhs_name == rhs_name && lhs_options == rhs_options && lhs_foobar == rhs_foobar.

What's the "best" way to implement this? (Best as in: (Runtime-)Efficiency, Maintainability, Readability)

  • operator== in terms of operator!=
  • operator!= in terms of operator==
  • Separate implementations for == and !=
  • As member or as free functions?

Note that this question is only about the (in)equality ops, as comparison (<, <=, ...) doesn't make too much sense for such aggregates.

like image 822
Martin Ba Avatar asked Feb 14 '11 11:02

Martin Ba


People also ask

What is the C equality operator?

The equality operators follow the same rules as the relational operators, but permit additional possibilities: a pointer can be compared to a constant integral expression with value 0, or to a pointer to void . If two pointers are both null pointers, they compare as equal.

What is comparison operators in C?

Comparison operators are binary operators that test a condition and return 1 if that condition is logically true and 0 if that condition is false.

How do you do comparisons in C?

We compare the strings by using the strcmp() function, i.e., strcmp(str1,str2). This function will compare both the strings str1 and str2. If the function returns 0 value means that both the strings are same, otherwise the strings are not equal.


4 Answers

I would do this but maybe move operator== definition to cpp file. Leave operator!= to be inline

Remember to compare member variables that are most likely to differ first so the rest are short-circuited and performance is better.

struct aggregate1 {
  bool operator==(const aggregate1& rhs) const
  {
     return (name == rhs.name)
     && (options == rhs.options)
     && (foobar == rhs.foobar);
  }
  bool operator!=(const aggregate1& rhs) const
  {
    return !operator==(rhs);
  }

  std::string name;
  std::vector<ValueT> options;
  size_t foobar;

  // ...
};
like image 70
T33C Avatar answered Sep 22 '22 16:09

T33C


Member or free function is a matter of taste, and writing separate implementations of == and != seems to me boring, error-prone (you may forget a member in just one of the two operators, and it will take time to notice) without adding anything in terms of efficiency (calling the other operator and applying ! has a negligible cost).

The decision is restricted to "is it better to implement operator== in terms of operator!= or the contrary?

In my opinion, in terms of maintainability/readability/efficiency it's the same; I'd only recommend to do it in the same way everywhere for the sake of consistency. The only case where you'd want to prefer to use one or the other as the "base operator" is when you know that, in the types contained in your structure, that operator is faster than its negation, but I don't know when this could happen.

like image 41
Matteo Italia Avatar answered Sep 23 '22 16:09

Matteo Italia


In C++20, implementing equality and inequality operators can be as simple as declaring operator== as default:

struct S {
  int x;
  // ...

  // As member function
  bool operator==(S const &) const = default;
  
  // As non-member function (hidden friend)
  // friend bool operator==(S const &, S const &) = default;
};

If only operator== is provided, a!=b is interpreted as !(a==b) according to overload resolution, so there is no need for providing an explicit overload for operator!=.

I would argue that defaulting operator== as a hidden friend is preferable because it works with reference-wrapped objects:

S s;
auto rs{std::ref(s)};
rs==rs; // OK for hidden friend; ill-formed if declared as member function

In this example, operator== is not defined for std::reference_wrapper<S>, but argument-dependent lookup (ADL) can select the hidden friend with operands implicitly-converted to S const &. Notice, however, that ::operator==(rs,rs) will only work if operator== is defined as a free function because ADL is not triggered for qualified names.

like image 32
hcb Avatar answered Sep 21 '22 16:09

hcb


IMHO, implement as friends and implement the operator== (some STL algorithms will rely on this for example) and the operator!= should be implemented as the negation of the equals operator.

like image 45
Nim Avatar answered Sep 23 '22 16:09

Nim