Search code examples
c++operatorsoperator-overloadingequality

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.


Solution

  • 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.