{"id":5923,"date":"2020-06-11T21:06:14","date_gmt":"2020-06-11T21:06:14","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-20-the-three-way-comparison-operator\/"},"modified":"2023-06-26T09:49:06","modified_gmt":"2023-06-26T09:49:06","slug":"c-20-the-three-way-comparison-operator","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-20-the-three-way-comparison-operator\/","title":{"rendered":"C++20: The Three-Way Comparison Operator"},"content":{"rendered":"<p>The three-way comparison operator <span style=\"font-family: courier new, courier;\">&lt;=&gt;<\/span> is often just called the spaceship operator. The spaceship operator determines whether A &lt; B, A = B, or A &gt; B for two values, A and B. You can define the spaceship operator, or the compiler can auto-generate it.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5199\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20.png\" alt=\"TimelineCpp20\" width=\"650\" height=\"250\" style=\"display: block; margin-left: auto; margin-right: auto;\" \/><\/p>\n<p>Let me start classically to appreciate the three-way comparison operator&#8217;s advantages.<\/p>\n<h2>Ordering before C++20<\/h2>\n<p>I implemented a simple <span style=\"font-family: courier new, courier;\">int<\/span> wrapper <span style=\"font-family: courier new, courier;\">MyInt<\/span>. Of course, I want to compare <span style=\"font-family: courier new, courier;\">MyInt<\/span>. Here is my solution using the <span style=\"font-family: courier new, courier;\">isLessThan<\/span> function template.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ comparisonOperator.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> MyInt {\r\n    <span style=\"color: #007788; font-weight: bold;\">int<\/span> value;\r\n    <span style=\"color: #006699; font-weight: bold;\">explicit<\/span> MyInt(<span style=\"color: #007788; font-weight: bold;\">int<\/span> val)<span style=\"color: #555555;\">:<\/span> value{val} { }\r\n    <span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">&lt;<\/span> (<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> {                  \r\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">&lt;<\/span> rhs.value;\r\n    }\r\n};\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> T<span style=\"color: #555555;\">&gt;<\/span>\r\nconstexpr <span style=\"color: #007788; font-weight: bold;\">bool<\/span> isLessThan(<span style=\"color: #006699; font-weight: bold;\">const<\/span> T<span style=\"color: #555555;\">&amp;<\/span> lhs, <span style=\"color: #006699; font-weight: bold;\">const<\/span> T<span style=\"color: #555555;\">&amp;<\/span> rhs) {\r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> lhs <span style=\"color: #555555;\">&lt;<\/span> rhs;\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> main() {\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>boolalpha <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n    MyInt <span style=\"color: #cc00ff;\">myInt2011<\/span>(<span style=\"color: #ff6600;\">2011<\/span>);\r\n    MyInt <span style=\"color: #cc00ff;\">myInt2014<\/span>(<span style=\"color: #ff6600;\">2014<\/span>);\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"isLessThan(myInt2011, myInt2014): \"<\/span>\r\n              <span style=\"color: #555555;\">&lt;&lt;<\/span> isLessThan(myInt2011, myInt2014) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n   std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The program works as expected:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5920\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/comparisonOperator.png\" alt=\"comparisonOperator\" width=\"400\" height=\"179\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/comparisonOperator.png 458w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/comparisonOperator-300x134.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/p>\n<p>Honestly, <span style=\"font-family: courier new, courier;\">MyInt<\/span> 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: &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/c-20-define-the-concept-regular-and-semiregular\">C++20: Define the Concept Regular and SemiRegular.<\/a>&#8220;<\/p>\n<p>Now, I have to write a lot of boilerplate code. Here are the missing five operators:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">==<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> { \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">==<\/span> rhs.value; \r\n}\r\n<span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">!=<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> { \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">!<\/span>(<span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span> <span style=\"color: #555555;\">==<\/span> rhs);    \r\n}\r\n<span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> { \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">!<\/span>(rhs <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>);     \r\n}\r\n<span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs)  <span style=\"color: #006699; font-weight: bold;\">const<\/span> { \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> rhs <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>;        \r\n}\r\n<span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&gt;=<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> {   \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">!<\/span>(<span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span> <span style=\"color: #555555;\">&lt;<\/span> rhs);     \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Done? No! I assume you want to compare <span style=\"font-family: courier new, courier;\">MyInt<\/span> with int&#8217;s. To support the comparison of an <span style=\"font-family: courier new, courier;\">int<\/span> and a <span style=\"font-family: courier new, courier;\">MyInt<\/span>, and a <span style=\"font-family: courier new, courier;\">MyInt<\/span> and an <span style=\"font-family: courier new, courier;\">int<\/span>, you have to overload each operator three times because the constructor is declared as <span style=\"font-family: courier new, courier;\">explicit<\/span>. Thanks to <span style=\"font-family: courier new, courier;\">explicit<\/span>, no implicit conversion from <span style=\"font-family: courier new, courier;\">int<\/span> to <span style=\"font-family: courier new, courier;\">MyInt<\/span> 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: &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-rules-for-overloading-and-overload-operators\">C++ Core Guidelines: Rules for Overloading and Overload Operators<\/a>&#8220;<\/p>\n<p>These are the three overloads for smaller-than.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">friend<\/span> <span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">&lt;<\/span> (<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInts<span style=\"color: #555555;\">&amp;<\/span> lhs, <span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) {                  \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> lhs.value <span style=\"color: #555555;\">&lt;<\/span> rhs.value;\r\n}\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">friend<\/span> <span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">&lt;<\/span> (<span style=\"color: #007788; font-weight: bold;\">int<\/span> lhs, <span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) {                  \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> lhs <span style=\"color: #555555;\">&lt;<\/span> rhs.value;\r\n}\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">friend<\/span> <span style=\"color: #007788; font-weight: bold;\">bool<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">&lt;<\/span> (<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInts<span style=\"color: #555555;\">&amp;<\/span> lhs, <span style=\"color: #007788; font-weight: bold;\">int<\/span> rhs) {                  \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> lhs.value <span style=\"color: #555555;\">&lt;<\/span> rhs;\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>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 <span style=\"font-family: courier new, courier;\">MyInt<\/span> and all operators should become <span style=\"font-family: courier new, courier;\">constexpr<\/span>. You should also consider making the operators <span style=\"font-family: courier new, courier;\">noexcept.<\/span><\/p>\n<p>I assume this is enough motivation for the three-way comparison operators.<\/p>\n<\/p>\n<h2>Ordering with C++20<\/h2>\n<p>You can define the three-way comparison operator or request it from the compiler with<span style=\"font-family: courier new, courier;\"> =default<\/span>. In both cases, you get all six comparison operators:&nbsp;<span style=\"font-family: courier new, courier;\">==,&nbsp;!=, &lt;, &lt;=, &gt;, <\/span>and<span style=\"font-family: courier new, courier;\"> &gt;=. <\/span><\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ threeWayComparison.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;compare&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> MyInt {\r\n    <span style=\"color: #007788; font-weight: bold;\">int<\/span> value;\r\n    <span style=\"color: #006699; font-weight: bold;\">explicit<\/span> MyInt(<span style=\"color: #007788; font-weight: bold;\">int<\/span> val)<span style=\"color: #555555;\">:<\/span> value{val} { }\r\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> {           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>      \r\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">&lt;=&gt;<\/span> rhs.value;\r\n    }\r\n};\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> MyDouble {\r\n    <span style=\"color: #007788; font-weight: bold;\">double<\/span> value;\r\n    <span style=\"color: #006699; font-weight: bold;\">explicit<\/span> constexpr MyDouble(<span style=\"color: #007788; font-weight: bold;\">double<\/span> val)<span style=\"color: #555555;\">:<\/span> value{val} { }\r\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyDouble<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n};\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> T<span style=\"color: #555555;\">&gt;<\/span>\r\nconstexpr <span style=\"color: #007788; font-weight: bold;\">bool<\/span> isLessThan(<span style=\"color: #006699; font-weight: bold;\">const<\/span> T<span style=\"color: #555555;\">&amp;<\/span> lhs, <span style=\"color: #006699; font-weight: bold;\">const<\/span> T<span style=\"color: #555555;\">&amp;<\/span> rhs) {\r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> lhs <span style=\"color: #555555;\">&lt;<\/span> rhs;\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> main() {\r\n    \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>boolalpha <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n    \r\n    MyInt <span style=\"color: #cc00ff;\">myInt1<\/span>(<span style=\"color: #ff6600;\">2011<\/span>);\r\n    MyInt <span style=\"color: #cc00ff;\">myInt2<\/span>(<span style=\"color: #ff6600;\">2014<\/span>);\r\n    \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"isLessThan(myInt1, myInt2): \"<\/span>\r\n              <span style=\"color: #555555;\">&lt;&lt;<\/span> isLessThan(myInt1, myInt2) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n              \r\n    MyDouble <span style=\"color: #cc00ff;\">myDouble1<\/span>(<span style=\"color: #ff6600;\">2011<\/span>);\r\n    MyDouble <span style=\"color: #cc00ff;\">myDouble2<\/span>(<span style=\"color: #ff6600;\">2014<\/span>);\r\n    \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"isLessThan(myDouble1, myDouble2): \"<\/span>\r\n              <span style=\"color: #555555;\">&lt;&lt;<\/span> isLessThan(myDouble1, myDouble2) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;          \r\n              \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n              \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The user-defined (1) and the compiler-generated (2) three-way comparison operator work as expected.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5921\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparison.PNG\" alt=\"threeWayComparison\" width=\"400\" height=\"157\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparison.PNG 1341w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparison-300x118.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparison-1024x402.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparison-768x302.png 768w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/p>\n<p>But there are a few subtle differences in this case. The compiler-deduced return type for <span style=\"font-family: courier new, courier;\">MyInt<\/span> (1) supports strong ordering, and the compiler-deduced return type of <span style=\"font-family: courier new, courier;\">MyDouble<\/span> supports partial ordering. Floating pointer numbers only <span style=\"font-family: courier new, courier;\"><\/span>support partial ordering because floating-point values such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/NaN\"><span style=\"font-family: courier new, courier;\">NaN<\/span><\/a> (Not a Number) can not be ordered. For example, <span style=\"font-family: courier new, courier;\">NaN<\/span> == <span style=\"font-family: courier new, courier;\">NaN<\/span>&nbsp; is <span style=\"font-family: courier new, courier;\">false<\/span>.<\/p>\n<p>I want to focus on this post about the compiler-generated spaceship operator.<\/p>\n<h3>The Compiler-Generated Spaceship Operator<\/h3>\n<p>The compiler-generated three-way comparison operator needs the header &lt;<span style=\"font-family: courier new, courier;\">compare&gt;<\/span>, which is implicit <span style=\"font-family: courier new, courier;\">constexpr<\/span> and <span style=\"font-family: courier new, courier;\">noexcept<\/span>. Additionally, it performs a lexicographical comparison. What? Let me start with <span style=\"font-family: courier new, courier;\">constexpr<\/span>.<\/p>\n<h4>Comparison at Compile-Time<\/h4>\n<p>The three-way comparison operator is implicit <span style=\"font-family: courier new, courier;\">constexpr<\/span>. Consequently, I simplify the previous program<span style=\"font-family: courier new, courier;\"> threeWayComparison.cpp<\/span> and compare <span style=\"font-family: courier new, courier;\">MyDouble<\/span> in the following program at compile-time.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ threeWayComparisonAtCompileTime.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;compare&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> MyDouble {\r\n    <span style=\"color: #007788; font-weight: bold;\">double<\/span> value;\r\n    <span style=\"color: #006699; font-weight: bold;\">explicit<\/span> constexpr MyDouble(<span style=\"color: #007788; font-weight: bold;\">double<\/span> val)<span style=\"color: #555555;\">:<\/span> value{val} { }\r\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyDouble<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;    \r\n};\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> T<span style=\"color: #555555;\">&gt;<\/span>\r\nconstexpr <span style=\"color: #007788; font-weight: bold;\">bool<\/span> isLessThan(<span style=\"color: #006699; font-weight: bold;\">const<\/span> T<span style=\"color: #555555;\">&amp;<\/span> lhs, <span style=\"color: #006699; font-weight: bold;\">const<\/span> T<span style=\"color: #555555;\">&amp;<\/span> rhs) {\r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> lhs <span style=\"color: #555555;\">&lt;<\/span> rhs;\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> main() {\r\n    \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>boolalpha <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n              \r\n    constexpr MyDouble <span style=\"color: #cc00ff;\">myDouble1<\/span>(<span style=\"color: #ff6600;\">2011<\/span>);\r\n    constexpr MyDouble <span style=\"color: #cc00ff;\">myDouble2<\/span>(<span style=\"color: #ff6600;\">2014<\/span>);\r\n    \r\n    constexpr <span style=\"color: #007788; font-weight: bold;\">bool<\/span> res <span style=\"color: #555555;\">=<\/span> isLessThan(myDouble1, myDouble2); <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n    \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"isLessThan(myDouble1, myDouble2): \"<\/span>\r\n              <span style=\"color: #555555;\">&lt;&lt;<\/span> res <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;          \r\n              \r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n              \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>I ask for the result of the comparison at compile-time (1), and I get it.<\/p>\n<h4><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5922\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonAtComileTime.PNG\" alt=\"threeWayComparisonAtComileTime\" width=\"450\" height=\"141\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonAtComileTime.PNG 1632w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonAtComileTime-300x94.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonAtComileTime-1024x322.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonAtComileTime-768x241.png 768w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonAtComileTime-1536x483.png 1536w\" sizes=\"auto, (max-width: 450px) 100vw, 450px\" \/><\/h4>\n<p>&nbsp;<\/p>\n<p>The compiler-generated three-way comparison operator performs a lexicographical comparison.&nbsp;<\/p>\n<h4>Lexicographical Comparison<\/h4>\n<p>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 <span style=\"font-family: courier new, courier;\">==<\/span> and <span style=\"font-family: courier new, courier;\">!=<\/span> operators behave differently in C++20. I will write about this exception to the rule in my next post.<\/p>\n<p>The post &#8220;<a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/simplify-your-code-with-rocket-science-c20s-spaceship-operator\/\">Simplify Your Code With Rocket Science: C++20\u2019s Spaceship Operator<\/a>&#8221; Microsoft C++ Team Blog provides an impressive example of the lexicographical comparison.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">struct<\/span> Basics {\r\n  <span style=\"color: #007788; font-weight: bold;\">int<\/span> i;\r\n  <span style=\"color: #007788; font-weight: bold;\">char<\/span> c;\r\n  <span style=\"color: #007788; font-weight: bold;\">float<\/span> f;\r\n  <span style=\"color: #007788; font-weight: bold;\">double<\/span> d;\r\n  <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> Basics<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\n};\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> Arrays {\r\n  <span style=\"color: #007788; font-weight: bold;\">int<\/span> ai[<span style=\"color: #ff6600;\">1<\/span>];\r\n  <span style=\"color: #007788; font-weight: bold;\">char<\/span> ac[<span style=\"color: #ff6600;\">2<\/span>];\r\n  <span style=\"color: #007788; font-weight: bold;\">float<\/span> af[<span style=\"color: #ff6600;\">3<\/span>];\r\n  <span style=\"color: #007788; font-weight: bold;\">double<\/span> ad[<span style=\"color: #ff6600;\">2<\/span>][<span style=\"color: #ff6600;\">2<\/span>];\r\n  <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> Arrays<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\n};\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> Bases <span style=\"color: #555555;\">:<\/span> Basics, Arrays {\r\n  <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;=&gt;<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> Bases<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\n};\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\r\n  constexpr Bases a <span style=\"color: #555555;\">=<\/span> { { <span style=\"color: #ff6600;\">0<\/span>, <span style=\"color: #cc3300;\">'c'<\/span>, <span style=\"color: #ff6600;\">1.f<\/span>, <span style=\"color: #ff6600;\">1.<\/span> },    <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n                        { { <span style=\"color: #ff6600;\">1<\/span> }, { <span style=\"color: #cc3300;\">'a'<\/span>, <span style=\"color: #cc3300;\">'b'<\/span> }, { <span style=\"color: #ff6600;\">1.f<\/span>, <span style=\"color: #ff6600;\">2.f<\/span>, <span style=\"color: #ff6600;\">3.f<\/span> }, { { <span style=\"color: #ff6600;\">1.<\/span>, <span style=\"color: #ff6600;\">2.<\/span> }, { <span style=\"color: #ff6600;\">3.<\/span>, <span style=\"color: #ff6600;\">4.<\/span> } } } };\r\n  constexpr Bases b <span style=\"color: #555555;\">=<\/span> { { <span style=\"color: #ff6600;\">0<\/span>, <span style=\"color: #cc3300;\">'c'<\/span>, <span style=\"color: #ff6600;\">1.f<\/span>, <span style=\"color: #ff6600;\">1.<\/span> },   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n                        { { <span style=\"color: #ff6600;\">1<\/span> }, { <span style=\"color: #cc3300;\">'a'<\/span>, <span style=\"color: #cc3300;\">'b'<\/span> }, { <span style=\"color: #ff6600;\">1.f<\/span>, <span style=\"color: #ff6600;\">2.f<\/span>, <span style=\"color: #ff6600;\">3.f<\/span> }, { { <span style=\"color: #ff6600;\">1.<\/span>, <span style=\"color: #ff6600;\">2.<\/span> }, { <span style=\"color: #ff6600;\">3.<\/span>, <span style=\"color: #ff6600;\">4.<\/span> } } } };\r\n  static_assert(a <span style=\"color: #555555;\">==<\/span> b);\r\n  static_assert(<span style=\"color: #555555;\">!<\/span>(a <span style=\"color: #555555;\">!=<\/span> b));\r\n  static_assert(<span style=\"color: #555555;\">!<\/span>(a <span style=\"color: #555555;\">&lt;<\/span> b));\r\n  static_assert(a <span style=\"color: #555555;\">&lt;=<\/span> b);\r\n  static_assert(<span style=\"color: #555555;\">!<\/span>(a <span style=\"color: #555555;\">&gt;<\/span> b));\r\n  static_assert(a <span style=\"color: #555555;\">&gt;=<\/span> b);\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>I assume the most complex aspect of the program is not the spaceship operator but the initialization of <span style=\"font-family: courier new, courier;\">Base<\/span> via aggregate initialization (1). Aggregate initialization enables it to directly initialize the members of a class type (<span style=\"font-family: courier new, courier;\">class, struct, union<\/span>) when the members are all <span style=\"font-family: courier new, courier;\">public<\/span>.&nbsp; In this case, you can use brace initialization. If you want to know more about aggregate initialization, <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/aggregate_initialization\">cppreference.com<\/a> 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.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>The compiler performs quite a clever job when it generates all operators. Ultimately, you get intuitive and efficient comparison operators for free. My<a href=\"https:\/\/www.modernescpp.com\/index.php\/c-20-more-details-to-the-spaceship-operator\"> next post <\/a>dives deeper into the magic under the hood.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The three-way comparison operator &lt;=&gt; is often just called the spaceship operator. The spaceship operator determines whether A &lt; B, A = B, or A &gt; B for two values, A and B. You can define the spaceship operator, or the compiler can auto-generate it.<\/p>\n","protected":false},"author":21,"featured_media":5199,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[375],"tags":[462],"class_list":["post-5923","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-spaceship"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5923","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=5923"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5923\/revisions"}],"predecessor-version":[{"id":6738,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5923\/revisions\/6738"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5199"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5923"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5923"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5923"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}