The Spaceship Operator and Expression Rewriting

Equality vs. Equivalence

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

Abstract art representing computer programming

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.

This Question is from the Lesson:

The Spaceship Operator and Expression Rewriting

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

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

The Spaceship Operator and Expression Rewriting

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

A computer programmer
Part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 124 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved