Equality vs. Equivalence

What are the differences between std::strong_ordering::equal and std::strong_ordering::equivalent?

In C++20, the <=> (spaceship) operator can return four possible values:

  • std::strong_ordering::less
  • std::strong_ordering::equal
  • std::strong_ordering::greater
  • std::strong_ordering::equivalent

Understanding the difference between equal and equivalent is crucial for certain comparisons.

std::strong_ordering::equal

This result is returned when two values are exactly the same. For instance, two integers with the same value or two strings that match exactly.

std::strong_ordering::equivalent

This result indicates that two values are considered equal in terms of ordering, but not necessarily identical. This can be useful in situations where you want to treat different representations as equivalent.

For example, case-insensitive string comparison or comparing pointers that point to the same object but have different addresses.

Here's an example with a custom string class that uses case-insensitive comparison:

#include <cctype>
#include <compare>
#include <iostream>
#include <string>

class CaseInsensitiveString {
 public:
  std::string Value;

  std::strong_ordering operator<=>(
    const CaseInsensitiveString& Other) const {
    auto it1 = Value.begin();
    auto it2 = Other.Value.begin();

    while (it1 != Value.end()
      && it2 != Other.Value.end()
    ) {
      char c1 = std::tolower(*it1);
      char c2 = std::tolower(*it2);
      if (c1 < c2)
        return std::strong_ordering::less;
      if (c1 > c2)
        return std::strong_ordering::greater;
      ++it1;
      ++it2;
    }

    if (Value.size() < Other.Value.size())
      return std::strong_ordering::less;
    if (Value.size() > Other.Value.size())
      return std::strong_ordering::greater;
    return std::strong_ordering::equivalent;
  }

  bool operator==(
    const CaseInsensitiveString& Other) const {
    return std::equal(
      Value.begin(), Value.end(),
      Other.Value.begin(), Other.Value.end(),
      [](char a, char b) {
        return std::tolower(a) == std::tolower(b);
      });
  }
};

int main() {
  CaseInsensitiveString A{"hello"};
  CaseInsensitiveString B{"HELLO"};

  if (A == B) {
    std::cout << "A is equal to B\n";
  }

  if ((A <=> B) == std::strong_ordering::equivalent) {
    std::cout << "A is equivalent to B\n";
  }
}
A is equal to B
A is equivalent to B

In this example, A and B are considered both equal and equivalent due to the case-insensitive comparison. The distinction between equal and equivalent allows for nuanced comparison logic in custom types.

The Spaceship Operator and Expression Rewriting

A guide to simplifying our comparison operators using C++20 features

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Defining the == Operator
Why do we need to explicitly define the == operator if we already have <=>?
Benefits of the Spaceship Operator
What are the benefits of using the spaceship operator <=> over traditional comparison operators?
Custom Comparison Logic
How can we implement custom comparison logic using the spaceship operator <=>?
Using <=> for Sorting
Can <=> be used for sorting algorithms in C++20?
Niche Cases for Three-Way Comparison
How does the C++20 standard handle types that do not naturally fit into the three-way comparison model?
Real-World Examples of <=>
What are some practical examples of using <=> in real-world applications?
Ask Your Own Question
Temporarily unavailable while we roll out updates. Back in a few days!