Search code examples
c++syntaxcomparisonoperatorsc++20

What's the benefit of 3-ways comparison operator (<=>) in C++20?


I know the syntax of it. I just wondering what's the benefit or whether does it make sens.

Without it, we must code like this:

void func1(int x, int y) {
    if( x > y )
        doSomeThing();
    else if( x < y )
        doSomeElse();
    else
        explosive();
}

With it, we can do like this:

void func1(int x, int y) {
    auto result = x <=> y;
    if( result > 0 )
        doSomeThing();
    else if( result < 0 )
        doSomeElse();
    else
        explosive();
}

Except for returning a comparison result, I can NOT see any benefit of this feature. Some one says it can make our codes more readable, but I don't think so. It's very obvious, the former example has more readability.

As to return a result, like this:

int func1(int x, int y) {
    return x <=> y;
}

It looks like that we get much more readability, but we still need to check the value with another if/else somewhere, e.g. outside of the func1.


Solution

  • I can NOT see any benefit of this feature.

    Then you are thinking too narrowly about what is actually happening.

    Direct use of <=> does not exist to serve the needs of ints. It can be used for comparing them, but ints are not why the functionality exists.

    It exists for types that actually have complex comparison logic. Consider std::string. To know if one string is "less than" another, you have to iterate through both strings and compare each character. When you find a non-equivalent one, you have your answer.

    Your code, when applied to string, does the comparison twice: once with less than and once with greater than. The problem is this: the first comparison already found the first non-equal character. But the second comparison does not know where that is. So it must start from the very beginning, doing the exact same comparisons the first one did.

    That's a lot of repeated work for an answer than the first comparison already computed. In fact, there is a really easy way to compute <=> for strings: subtract the corresponding character in the second string from the first. If the value is zero, then they are equal. If the result is negative, the first string is less; if it's positive, the first string is greater.

    Which is... exactly what <=> returns, isn't it? By using <=>, you do two expensive comparisons in the space of one; testing the return value is immaterial next to the cost of them.

    The more complex your comparison logic, the more you are likely to save with <=> if you need to categorize them into less/greater/equal.

    It should also be noted that processors often have special opcodes to tell if an integer is negative, zero, or positive. If we look at the x86 assembly for your integer comparison:

    func1(int, int):                             # @func1(int, int)
            cmp     edi, esi
            jle     .LBB0_1
            jmp     a()@PLT                       # TAILCALL
    .LBB0_1:
            jge     .LBB0_2
            jmp     b()@PLT                       # TAILCALL
    .LBB0_2:
            jmp     c()@PLT                       # TAILCALL
    

    We can see that it only executes cmp once; the jle and jge instructions use the results of the comparison. The <=> compiles to the same assembly, so the compiler fully understands these as being synonyms.