C++ Spaceship operator usage


C++20 introduced a new three-way comparison operator, also known as the spaceship operator. Here I explain how it works and show some simple examples.

First, let’s take a look at some code, which I soon will explain:

#include <compare>
#include <iostream>

int main() {
    constexpr int m{1};
    constexpr int n{2};
    constexpr auto res{ m <=> n };

    if (std::is_lt(res)) {
        std::cout << m << " is less than " << n << std::endl;
    }
    else if (std::is_gt(res)) {
        std::cout << m << " is greater than " << n << std::endl;
    }
    else if (std::is_eq(res)) {
        std::cout << m << " is equal to " << n << std::endl;
    }
}

This code shows a simple three-way comparison of two integers. Since comparing the integers can have three different outcomes, either the first one is larger, smaller, or the two are equal, the type of the resulting comparison must be something else than a boolean. So what does a spaceship comparison actually return?

When, like in this case, the operands are integral types, the result is a so-called strong ordering and can be one of the following:

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

If you wish to be more explicit about the types you can change the code above to look like this:

int main() {
    constexpr int m{1};
    constexpr int n{2};
    constexpr std::strong_ordering res{ m <=> n };

    if (res == std::strong_ordering::less) {
        std::cout << m << " is less than " << n << std::endl;
    }
    else if (res == std::strong_ordering::greater) {
        std::cout << m << " is greater than " << n << std::endl;
    }
    else if (res == std::strong_ordering::equal) {
        std::cout << m << " is equal to " << n << std::endl;
    }
}

If the operands are floating-point types the result is a partial ordering:

  • std::partial_ordering::less
  • std::partial_ordering::greater
  • std::partial_ordering::equal
  • std::partial_ordering::unordered

The last one might be confusing, how can a comparison between two floating-point values be unordered? This happens if one (or both) operands are not-a-number (NaN). NaN is a special value that a floating number is assigned as a result of things like taking the square root of a negative number or dividing 0 by 0:

#include <cmath>
#include <compare>
#include <iostream>

int main() {
    constexpr double m{1.1};
    const double n{sqrt(-1)}; // n is NaN
    const auto res{ m <=> n };

    if (res == std::partial_ordering::unordered) {
        std::cout << m << " <=> " << n << " is unordered" << std::endl;
    }
}

There is also a weak ordering that none of the comparisons between fundamental types return but you can choose to use when implementing three-way comparison for your own types:

  • std::weak_ordering::less
  • std::weak_ordering::greater
  • std::weak_ordering::equal

As can be seen from these examples there is not much gained from using the spaceship operator for fundamental types. In those cases you can stick to the normal <, >, ==, <=, >= operators. However, if you have your own defined types that are expensive to compare it can be useful to be able to do a single comparison instead of several.

In an upcoming post I will describe how to implement the spaceship operator for your own types.

,

Lämna ett svar

Din e-postadress kommer inte publiceras.