Why Does the Default Operator Give Us Operator == for Free, but Custom Implementation Does Not?
Image by Chepziba - hkhazo.biz.id

Why Does the Default Operator <=> Give Us Operator == for Free, but Custom Implementation Does Not?

Posted on

Have you ever wondered why the default implementation of the <=> operator in C++20 gives us operator == for free, but when we implement it ourselves, it doesn’t magically provide the equality operator? If so, you’re in the right place! In this article, we’ll dive into the world of spaceship operators, default implementations, and the magic behind the scenes.

What is the <=> Operator?

The <=> operator, commonly known as the spaceship operator, is a new addition to the C++20 standard. It’s a three-way comparison operator that returns an object of type std::partial_ordering, which indicates the relationship between two objects. The operator can return one of three values:

  • std::partial_ordering::less: The left operand is less than the right operand.
  • std::partial_ordering::equivalent: The left operand is equivalent to the right operand.
  • std::partial_ordering::greater: The left operand is greater than the right operand.

The <=> operator is a powerful tool for implementing comparison logic in your classes, and it’s especially useful when combined with the default implementation.

The Default Implementation

In C++20, the compiler can automatically generate the implementation of the <=> operator for your class, if you don’t provide a custom implementation. This default implementation is based on the members of your class and their own <=> implementations. The compiler recursively applies the <=> operator to each member, using a lexicographic comparison approach.

struct Person {
  std::string name;
  int age;
};

Person p1 = {"John", 30};
Person p2 = {"Jane", 25};

auto result = (p1 <=> p2); // Compiler-generated implementation

In this example, the compiler generates an implementation of the <=> operator for the Person class. The implementation compares the name members lexicographically, and then compares the age members. The result is a std::partial_ordering object that indicates the relationship between p1 and p2.

Why Does the Default Implementation Give Us Operator == for Free?

When the compiler generates the default implementation of the <=> operator, it also provides an implementation of the == operator, based on the <=> operator. This is because the == operator can be defined in terms of the <=> operator:

bool operator==(const Person& lhs, const Person& rhs) {
  return (lhs <=> rhs) == std::partial_ordering::equivalent;
}

This implementation is valid because the <=> operator returns std::partial_ordering::equivalent when the operands are equal. By using the <=> operator as a building block, the compiler can generate an efficient and correct implementation of the == operator.

Why Doesn’t Custom Implementation Give Us Operator == for Free?

Now, let’s consider what happens when we provide a custom implementation of the <=> operator. Suppose we want to implement a custom comparison logic for the Person class:

struct Person {
  std::string name;
  int age;

  auto operator<=>(const Person& rhs) const {
    if (age != rhs.age) {
      return age <=> rhs.age;
    }
    return name <=> rhs.name;
  }
};

In this example, we’ve provided a custom implementation of the <=> operator, which compares the age members first, and then the name members. However, we haven’t implemented the == operator explicitly.

Unlike the default implementation, our custom implementation doesn’t give us the == operator for free. This is because we’ve taken control of the comparison logic, and the compiler no longer generates an implementation of the == operator based on the <=> operator.

This means that if we want to use the == operator with our custom implementation, we need to provide an explicit implementation:

bool operator==(const Person& lhs, const Person& rhs) {
  return (lhs <=> rhs) == std::partial_ordering::equivalent;
}

How to Get Operator == for Free with Custom Implementation?

So, is there a way to get the == operator for free with a custom implementation of the <=> operator? The answer is yes! We can use the = default syntax to request the compiler-generated implementation of the == operator, even when we provide a custom implementation of the <=> operator:

struct Person {
  std::string name;
  int age;

  auto operator<=>(const Person& rhs) const {
    if (age != rhs.age) {
      return age <=> rhs.age;
    }
    return name <=> rhs.name;
  }

  bool operator==(const Person& rhs) const = default;
};

By using = default, we’re telling the compiler to generate the implementation of the == operator, based on the <=> operator. This way, we get the benefits of a custom implementation of the <=> operator, while still getting the == operator for free.

Conclusion

In this article, we’ve explored the magic behind the default implementation of the <=> operator and how it provides the == operator for free. We’ve also seen why custom implementations don’t get the == operator automatically, and how to request the compiler-generated implementation using the = default syntax.

Remember, when working with the <=> operator, it’s essential to understand the default implementation and how it interacts with custom implementations. By leveraging the power of the <=> operator, you can write more efficient and expressive comparison logic in your C++20 code.

Operator Description
<=> Three-way comparison operator
== Equality operator
= default Syntax to request compiler-generated implementation

Now, go forth and spaceship your way to better C++ code!

Frequently Asked Question

Unravel the mysteries of the default operator <=> and custom implementation in C++!

Why does the default operator <=> give us operator == for free?

When you implement the default operator <=>, the compiler takes care of generating the operator == for you, because it’s a natural consequence of the three-way comparison. This is a clever design choice in C++20, which allows you to write less code and still get the benefits of a complete set of comparison operators!

What’s the magic behind the default operator <=> giving us operator == for free?

The secret lies in the way the compiler generates the equality operator (==) from the three-way comparison operator (<=>). When you define <=>, the compiler can automatically derive the == operator by checking for equality in the neutral (EQ) case. This makes life easier for developers, who can focus on implementing the more complex logic of the three-way comparison!

Why doesn’t a custom implementation of operator <=> give us operator == for free?

That’s because a custom implementation of operator <=> doesn’t provide the same guarantees as the default implementation. When you write a custom <=>, you’re telling the compiler that you want to handle the three-way comparison logic yourself. In this case, the compiler can’t assume that the == operator can be derived automatically, so you need to implement it explicitly!

How can I take advantage of the default operator <=> and get operator == for free?

To reap the benefits of the default operator <=>, you need to ensure that your class satisfies the constraints for a ‘regular type’. This means your class should have a publicly accessible, non-ambiguous, and non- deleted default constructor, copy constructor, and move constructor, as well as a publicly accessible destructor. By following these guidelines, you’ll get the default <=> operator and, as a bonus, the == operator will be generated for you!

What’s the main takeaway from the default operator <=> vs custom implementation?

The key insight is that the default operator <=> and custom implementation have different design goals and constraints. The default implementation assumes a ‘regular type’ and provides a complete set of comparison operators, including ==, for free. In contrast, a custom implementation gives you full control over the three-way comparison logic but requires explicit implementation of the == operator. Choose wisely, depending on your specific use case and requirements!

Leave a Reply

Your email address will not be published. Required fields are marked *