The three-way comparison operator <=> is often just called the spaceship operator. The spaceship operator determines whether A < B, A = B, or A > B for two values, A and B. You can define the spaceship operator, or the compiler can auto-generate it.
Let me start classically to appreciate the three-way comparison operator’s advantages.
Ordering before C++20
I implemented a simple int wrapper MyInt. Of course, I want to compare MyInt. Here is my solution using the isLessThan function template.
The program works as expected:
Honestly, MyInt is an unintuitive type. When you define one of the six ordering relations, you should define all. Intuitive types should be at least semi-regular: “C++20: Define the Concept Regular and SemiRegular.“
Now, I have to write a lot of boilerplate code. Here are the missing five operators:
Done? No! I assume you want to compare MyInt with int’s. To support the comparison of an int and a MyInt, and a MyInt and an int, you have to overload each operator three times because the constructor is declared as explicit. Thanks to explicit, no implicit conversion from int to MyInt kicks in. For convenience, you make the operators to a friend of the class. If you need more background information for my design decisions, read my previous post: “C++ Core Guidelines: Rules for Overloading and Overload Operators“
These are the three overloads for smaller-than.
This means, in total, that you have to implement 18 comparison operators. Is this the end of the story? Maybe not, because you decided that the MyInt and all operators should become constexpr. You should also consider making the operators noexcept.
I assume this is enough motivation for the three-way comparison operators.
Ordering with C++20
You can define the three-way comparison operator or request it from the compiler with =default. In both cases, you get all six comparison operators: ==, !=, <, <=, >, and >=.
The user-defined (1) and the compiler-generated (2) three-way comparison operator work as expected.
But there are a few subtle differences in this case. The compiler-deduced return type for MyInt (1) supports strong ordering, and the compiler-deduced return type of MyDouble supports partial ordering. Floating pointer numbers only support partial ordering because floating-point values such as NaN (Not a Number) can not be ordered. For example, NaN == NaN is false.
I want to focus on this post about the compiler-generated spaceship operator.
The Compiler-Generated Spaceship Operator
The compiler-generated three-way comparison operator needs the header <compare>, which is implicit constexpr and noexcept. Additionally, it performs a lexicographical comparison. What? Let me start with constexpr.
Comparison at Compile-Time
The three-way comparison operator is implicit constexpr. Consequently, I simplify the previous program threeWayComparison.cpp and compare MyDouble in the following program at compile-time.
I ask for the result of the comparison at compile-time (1), and I get it.
The compiler-generated three-way comparison operator performs a lexicographical comparison.
Lexicographical comparison means that all base classes are compared left to right and all non-static members in their declaration order. I have to qualify: for performance reasons, the compiler-generated == and != operators behave differently in C++20. I will write about this exception to the rule in my next post.
The post “Simplify Your Code With Rocket Science: C++20’s Spaceship Operator” Microsoft C++ Team Blog provides an impressive example of the lexicographical comparison.
I assume the most complex aspect of the program is not the spaceship operator but the initialization of Base via aggregate initialization (1). Aggregate initialization enables it to directly initialize the members of a class type (class, struct, union) when the members are all public. In this case, you can use brace initialization. If you want to know more about aggregate initialization, cppreference.com provides more information. I will write more about aggregate initialization in a future post when I will have a closer look at designated initialization in C++20.
The compiler performs quite a clever job when it generates all operators. Ultimately, you get intuitive and efficient comparison operators for free. My next post dives deeper into the magic under the hood.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, and Marco Parri Empoli.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
|My special thanks to Embarcadero|
|My special thanks to PVS-Studio|
|My special thanks to Tipi.build|
|My special thanks to Take Up Code|
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,