How to overload spaceship operator (<=>) with reversed order?

337 views Asked by At

I'd like to implement operator<=> to a custom type Bar with a reversed ordering.

struct Bar
{
    auto operator<=>(const Bar& rhs) const
    {
        // How to implement <=> on `int i`
        // But with reversed order
        // i.e., Bar{1} > Bar{2}
        
        // This definitely does not work
        return !(a <=> rhs.a);
    }
    int i;
};

Is there simpler way than using a switch-case to enumerate all the cases?

3

There are 3 answers

0
Barry On BEST ANSWER

The other answers already point out that if you want to reverse x <=> y than the answer is simply y <=> x. We can just make a table and verify this:

x <=> y y <=> x
x == y equal equal
x < y less greater
x > y greater less

That's the easiest approach, just reverse the operands - since that's what you want to do, reverse the operation, by reversing the operands.

Another approach, which is useful in the case where you don't have the operands but do have the result (which is less likely but does come up) is 0 <=> (x <=> y):

x <=> y 0 <=> (x <=> y)
equal equal
less greater
greater less

Conceptually, you can think of <=> as returning -1, 0, or 1 (it doesn't, it returns a class type, but sometimes a small fiction helps us understand), so for instance you can think of 0 <=> less as basically doing 0 <=> -1 which is greater, and likewise 0 <=> greater as basically doing 0 <=> 1 which is less. You get the right answer, so as long as you don't fall into the trap of really thinking that these are ints and wanting to do more int math on them, you'll be fine.

0
Niall On

I think it is important to note the order doesn’t need the be “reversed” as much as the operator needs an implementation that matches the semantics you want or need in the class. I do note that the example is likely a minimised version of the situation you face and the “reverse” is that you want the ordering to be opposite of what the natural ordering is for integers.

The order of the comparison you are asking for can be done by reversing the order of the comparison of the a member;

// ...
auto operator<=>(const Bar& rhs) const
{
    return rhs.a <=> a;
}
// ...

Other changes can be made, but outside the scope of the immediate question.

0
Jan Schultke On

The <=> operator isn't commutative. Reversing the order of operands would also flip greater/less.

struct Bar
{
    // note: overloading as a hidden friend is better than as a member
    //       function because it makes the function symmetrical regarding
    //       implicit conversions of arguments, and <=> is expected to be symmetrical
    friend auto operator<=>(const Bar& lhs, const Bar& rhs) noexcept
    {
        return rhs.i <=> lhs.i;
    }
    int i;
};

That being said, the behavior would be counter-intuitive because < is delegated to <=> automatically, and it would mean that Bar{3} < Bar{1}.

If you want to reverse a comparison case-by-case, do it by using std::ranges::greater instead of std::ranges::less when using an algorithm. The <=> overload should still follow intuition, and a reversed one wouldn't.


See also What are the basic rules and idioms for operator overloading?.