c++switch-statementc++20spaceship-operator

How do I use the spaceship operator in a switch statement


The new <=> operator makes writing code more convenient and it can save some performance if the comparison algorithm is non-trivial, because it doesn't need to be repeated two times to get the full ordering.

Or at least I though so when I learned about it. However, when I try to use it in practice, in a switch statement, it doesn't work.

This code doesn't compile:

#include <iostream>

void compare_values(int x, int y)
{
    switch (x <=> y)
    {
    case std::strong_ordering::less:
        std::cout << "is less\n";
        break;
    case std::strong_ordering::greater:
        std::cout << "is greater\n";
        break;
    case std::strong_ordering::equal:
        std::cout << "is equal\n";
        break;
    }
}

The compiler shows an error suggesting that the value returned by <=> cannot be used in a switch:

<source>: In function 'void compare_values(int, int)':
<source>:5:15: error: switch quantity not an integer
    5 |     switch (x <=> y)
      |             ~~^~~~~
Compiler returned: 1

(live example)

I would guess that using the spaceship operator in switch is a pretty basic, obvious and common use case, so there is probably some trick to making it work. I cannot figure it out, though.

How can I fix this code?


Solution

  • The problem is that the spaceship operator (formally known as three-way comparison) does not return an ingeral type, and therefore cannot be used in a switch-case statement.

    In this case of comparing types like ints, the spaceship operator returns a std::strong_ordering.

    (A side note: as you can see in the documentation there are cases where the spaceship operator returns a std::partial_ordering but this isn't an integral type either).

    You can use it in an if-else statement instead.

    If you prefer to use a switch-case, you can use a trivial utility that converts the std::strong_ordering to an integral type with some predefined values.
    Returning -1/0/1 in this case will be quite natural:

    #include <iostream>
    
    constexpr int strong_ordering_to_int(std::strong_ordering o)
    {
        if (o == std::strong_ordering::less)    return -1;
        if (o == std::strong_ordering::greater) return 1;
        return 0;
    }
    
    void compare_values(int x, int y)
    {
        switch (strong_ordering_to_int(x <=> y))
        {
        case -1:
            std::cout << "is less\n";
            break;
        case 1:
            std::cout << "is greater\n";
            break;
        case 0:
            std::cout << "is equal\n";
            break;
        }
    }
    
    int main()
    {
        compare_values(2, 3);
        compare_values(3, 2);
        compare_values(3, 3);
    }
    

    Output:

    is less
    is greater
    is equal
    

    Live demo