2

In a slight variation of this question. I would like to define a custom type with a custom <=> operator and use that custom <=> operator to generate a ==. Trying the following

#include <compare>
#include <iostream>
#include <cassert>

struct widget {
  int member;
  int non_comparison_data;
  friend std::strong_ordering operator<=>(const widget& lhs,
                                          const widget& rhs) {
    std::cout << "doing a three way comparison" << std::endl;
    return lhs.member <=> rhs.member;
  }
//   friend bool operator==(const widget& lhs, const widget& rhs) {
//     return 0 == (lhs <=> rhs);
//   }
  friend bool operator==(const widget& lhs, const widget& rhs) = default;
};

int main() {
  widget a{.member = 1, .non_comparison_data = 23};
  widget b{.member = 1, .non_comparison_data = 42};
  assert(a==b);
  return 0;
}

I observe that a default == operator does not use the custom <=> operator, but instead does what would be done in absence of any custom comparison and compares all data members. I can define an operator == on my own that uses <=> like follows, but I'm wondering if there's a better way to get == out of <=>.

friend bool operator==(const widget& lhs, const widget& rhs) {{
  return 0 == (lhs <=> rhs);
}

PS: compiler-explorer link

PS: I'm aware a custom <=> doesn't generate a default == because == is likely implementable in a more optimal way than using <=> and one does not want an inefficient default to be generated.

pseyfert
  • 3,263
  • 3
  • 21
  • 47

2 Answers2

1

I'm wondering if there's a better way to get == out of <=>.

Nope, there's not. What you did (return 0 == (lhs<=>rhs);) is the optimal way. What more were you looking for?

Tumbleweed53
  • 1,491
  • 7
  • 13
1

The only way to define == in terms of <=> is to do it manually. Which is what you're already doing.

You can do a little bit better by writing member functions (the only benefit of non-member friends would be if you wanted to take by value. But if you're not doing that, member functions are easier - simply less code to write). And then implement == forwards instead of backwards... no Yoda conditionals please.

struct widget {
  int member;
  int non_comparison_data;

  std::strong_ordering operator<=>(const widget& rhs) const {
    return member <=> rhs.member;
  }

  bool operator==(const widget& rhs) const {
      return (*this <=> rhs) == 0;
  }
};
Barry
  • 286,269
  • 29
  • 621
  • 977
  • for the `friend` part, i was told in review to keep binary operators free `friend` functions in compliance with core guideline C.86 https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-eq – pseyfert Dec 27 '20 at 22:20
  • @pseyfert The description there says "`B`’s comparison accepts conversions for its second operand, but not its first" but that is no longer true in C++20. So the guideline (make `==` symmetric) is trivially satisfied with member functions (as long as you remember both `const`s). – Barry Dec 27 '20 at 22:39
  • bool operator == using operator <=>; in C++23 please ;) – NoSenseEtAl Feb 16 '21 at 19:06
  • @NoSenseEtAl No. – Barry Feb 16 '21 at 19:13