16 comments

[ 3.8 ms ] story [ 55.7 ms ] thread
Can someone explain the purpose of "Three-way comparison (“spaceship”) and defaulted comparisons"

>>>The value of the expression a <=> b describes which one of a < b, a == b, and a > b is true.

A key part of this feature is that classes that provide this operator can have all of the six traditional, binary relational operators defined implicitly.

The primary purpose of this is reducing boilerplate code. For supporting comparisons one currently has to implement a large set if functions. One for each of <, >, <=, =>, != and ==. However many times the implementation of those 6 functions is similar. Thus lots of code to write.

With operator<=> one can implement a single function and the compiler becomes the smart one. When calling o1 < o2 or calling o1 >= o2 the compiler will do a call to the single operator<=> and interpret the return value accordingly.

Notably less repitive typing for the type provider.

What if you want to do an equality operation, but the greater than or less than operators require lots of computation? If an object only defines the space ship operator, then isn't it very likely that you will get a lot of extra computation when all you care baout if the two objects are or aren't equal to each other?
You can choose not to define the spaceship operator and keep using the “old” ones if you want.
Specialized Operators have higher priority. If available they will be called.
I read that to mean that it saves you from writing highly redundant operator overloads for comparable classes.

You can think of two types of comparison:

* Those that return a boolean. eg. operator < returns bool, true if less than the operand, false if greater or equal.

* Those that return a 3-way value. An example of this would be strcmp(), which might return -1 if less, 0 if equal, and 1 if greater.

The insight is that if the compiler knows the latter exists, it can fill in the former category, and the programmer doesn't have to write a bunch of extra overloads.

One of the goals is to eliminate boilerplate. If I'm remembering my C++ correctly,!=, <=, >, and >= can be easily defined in terms of < and ==, but programmers were required to spell out the definitions for all the operators manually. With operator<=>, one definition would allow the compiler to generate all 6 operators.

There are some correctness and efficiency concerns operator<=> addresses, too, but I'm not confident I can explain those well.

You can read more in P0515R0 [0], which sort of "rolls up" previous papers discussing comparisons.

[0]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p051...

Technically you don't even need both '<' and '=='. Just '<=' will do if you follow the mathematical definitions (i.e. a <= b and b <= a implies a == b). This is not always the most efficient, but it's not the worst option.
Ah, you're right. I don't see programmers defining the operators that way very frequently for one reason or another, but perhaps I just need to be exposed to more code bases.
One of the useful things added is that the language can now define a default <=> operator for your own classes, for example if you had struct Foo {int a; int b;} previously you'd have had to define your own comparison operators such as:

bool operator<(const Foo& foo) const { if (a < foo.a) return true; if (a > foo.a) return false; if (b < foo.b) return true; if (b > foo.b) return false; return false; }

.. and all the other comparison operators that you needed.

But now, you can just say:

auto operator<=>(const Foo&) const = default;

and the compiler will automatically generate all the operators for you.

At the bottom of the page, I had to chuckle at:

Assorted snippets demonstrating C++20

    int main() {}
There is no return statement, although function is marked as `int`
C and C++ explicitly permit this
I remember problems compiling this in older C++, circa 2000. Maybe was just a compiler's "opinion"...
Probably just a buggy implementation. The C and C++ standards are explicit that `main` return an int and only an int. Never void. This has always been the case. However, the `main` function is the only function where the return statement can be omitted. If it is omitted, it’s an implicit `return 0`.