{"id":5931,"date":"2020-06-25T16:42:27","date_gmt":"2020-06-25T16:42:27","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-20-fast-comparison-with-the-spaceship-operator\/"},"modified":"2023-12-06T13:36:20","modified_gmt":"2023-12-06T13:36:20","slug":"c-20-fast-comparison-with-the-spaceship-operator","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-20-fast-comparison-with-the-spaceship-operator\/","title":{"rendered":"C++20: Optimized Comparison with the Spaceship Operator"},"content":{"rendered":"<p>In this post, I conclude my miniseries on the three-way comparison operator with a few subtle details. The subtle details include the compiler-generated <span style=\"font-family: courier new, courier;\">==<\/span> and <span style=\"font-family: courier new, courier;\">!=<\/span> operators and the interplay of classical comparison operators, and the three-way comparison operator.<\/p>\n<p><!--more--><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5199\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20.png\" alt=\"TimelineCpp20\" width=\"650\" height=\"250\" \/><\/p>\n<p>I finished my last post, &#8220;<a href=\"https:\/\/bit.ly\/MoreDetailsToSpaceship\">C++20: More Details to the Spaceship Operator<\/a>&#8221; with the following class <span style=\"font-family: courier new, courier;\">MyInt<\/span>. In this concrete case, I promised to elaborate more on the difference between an explicit and a non-explicit constructor. The rule of thumb is that a constructor taking one argument should be explicit.<\/p>\n<h3>Explicit Constructor<\/h3>\n<p>Here is essentially the user-defined type <span style=\"font-family: courier new, courier;\">MyInt<\/span> from my last post.<\/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;\">\/\/ threeWayComparisonWithInt2.cpp<\/span>\n\n<span style=\"color: #009999;\">#include &lt;compare&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">MyInt<\/span> {\n <span style=\"color: #9999ff;\">public:<\/span>\n    constexpr <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} { }    <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\n    \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: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\n    \n    constexpr <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> <span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> {   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">&lt;=&gt;<\/span> rhs;\n    }\n    \n <span style=\"color: #9999ff;\">private:<\/span> \n    <span style=\"color: #007788; font-weight: bold;\">int<\/span> value;\n};\n\n\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\n    \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;\n    \n    constexpr MyInt myInt2011(<span style=\"color: #ff6600;\">2011<\/span>);\n    constexpr MyInt myInt2014(<span style=\"color: #ff6600;\">2014<\/span>);\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; myInt2014: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> myInt2014) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl; <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/span>\n\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; 2014: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #ff6600;\">2014<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (5)<\/span>\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; 2014.5: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #ff6600;\">2014.5<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;       <span style=\"color: #0099ff; font-style: italic;\">\/\/ (6)<\/span>\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; true: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #336666;\">true<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (7)<\/span>\n              \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n              \n}\n<\/pre>\n<\/div>\n<p>Constructors taking one argument such as (1) are often called conversion constructors because they can generate, such as in this case an instance of <span style=\"font-family: courier new, courier;\">MyInt<\/span> from an <span style=\"font-family: courier new, courier;\">int<\/span>.<\/p>\n<p><span style=\"font-family: courier new, courier;\">MyInt<\/span> has an explicit constructor (1), a compiler-generated three-way comparison operator (2), and a user-defined comparison operator for <span style=\"font-family: courier new, courier;\">int<\/span>(3).\u00a0 (4) uses the compiler-generated comparison operator for <span style=\"font-family: courier new, courier;\">MyInt<\/span>, and (5, 6, and 7) the user-defined comparison operator for int. Thanks to implicit narrowing to <span style=\"font-family: courier new, courier;\">int<\/span> (6) and the integral promotion (7), instances of <span style=\"font-family: courier new, courier;\">MyInt<\/span> can be compared with <span style=\"font-family: courier new, courier;\">double<\/span> values and <span style=\"font-family: courier new, courier;\">bool<\/span> values.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5928\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonMyInt.png\" alt=\"threeWayComparisonMyInt\" width=\"250\" height=\"95\" \/><\/p>\n<p>When I make <span style=\"font-family: courier new, courier;\">MyInt<\/span> more<span style=\"font-family: courier new, courier;\"> int<\/span>-like, the benefit of the explicit constructor (1) becomes apparent. In the following example, <span style=\"font-family: courier new, courier;\">MyInt<\/span> supports basic arithmetic.<\/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;\">\/\/ threeWayComparisonWithInt4.cpp<\/span>\n\n<span style=\"color: #009999;\">#include &lt;compare&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">MyInt<\/span> {\n <span style=\"color: #9999ff;\">public:<\/span>\n    constexpr <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} { }               <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\n    \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: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;  \n    \n    constexpr <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> <span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&amp;<\/span> rhs) <span style=\"color: #006699; font-weight: bold;\">const<\/span> {\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">&lt;=&gt;<\/span> rhs;\n    }\n    \n    constexpr <span style=\"color: #006699; font-weight: bold;\">friend<\/span> MyInt <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> a, <span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> b){\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> MyInt(a.value <span style=\"color: #555555;\">+<\/span> b.value);\n    }\n    \n    constexpr <span style=\"color: #006699; font-weight: bold;\">friend<\/span> MyInt <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> a,<span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> b){\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> MyInt(a.value <span style=\"color: #555555;\">-<\/span> b.value);\n    }\n    \n    constexpr <span style=\"color: #006699; font-weight: bold;\">friend<\/span> MyInt <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> a, <span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> b){\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> MyInt(a.value <span style=\"color: #555555;\">*<\/span> b.value);\n    }\n    \n    constexpr <span style=\"color: #006699; font-weight: bold;\">friend<\/span> MyInt <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> a, <span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> b){\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> MyInt(a.value <span style=\"color: #555555;\">\/<\/span> b.value);\n    }\n    \n    <span style=\"color: #006699; font-weight: bold;\">friend<\/span> std<span style=\"color: #555555;\">::<\/span>ostream<span style=\"color: #555555;\">&amp;<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span><span style=\"color: #555555;\">&lt;&lt;<\/span> (std<span style=\"color: #555555;\">::<\/span>ostream <span style=\"color: #555555;\">&amp;<\/span>out, <span style=\"color: #006699; font-weight: bold;\">const<\/span> MyInt<span style=\"color: #555555;\">&amp;<\/span> myInt){\n        out <span style=\"color: #555555;\">&lt;&lt;<\/span> myInt.value;\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> out;\n    }\n    \n <span style=\"color: #9999ff;\">private:<\/span> \n    <span style=\"color: #007788; font-weight: bold;\">int<\/span> value;\n};\n\n\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\n    \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;\n    \n    constexpr MyInt myInt2011(<span style=\"color: #ff6600;\">2011<\/span>);\n    constexpr MyInt myInt2014(<span style=\"color: #ff6600;\">2014<\/span>);\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; myInt2014: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> myInt2014) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; 2014: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #ff6600;\">2014<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; 2014.5: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #ff6600;\">2014.5<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"myInt2011 &lt; true: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> (myInt2011 <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #336666;\">true<\/span>) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n    constexpr MyInt res1 <span style=\"color: #555555;\">=<\/span> (myInt2014 <span style=\"color: #555555;\">-<\/span> myInt2011) <span style=\"color: #555555;\">*<\/span> myInt2011;   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"res1: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> res1 <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n    constexpr MyInt res2 <span style=\"color: #555555;\">=<\/span> (myInt2014 <span style=\"color: #555555;\">-<\/span> myInt2011) <span style=\"color: #555555;\">*<\/span> <span style=\"color: #ff6600;\">2011<\/span>;        <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"res2: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> res2 <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n    constexpr MyInt res3 <span style=\"color: #555555;\">=<\/span> (<span style=\"color: #336666;\">false<\/span> <span style=\"color: #555555;\">+<\/span> myInt2011 <span style=\"color: #555555;\">+<\/span> <span style=\"color: #ff6600;\">0.5<\/span>)  <span style=\"color: #555555;\">\/<\/span> <span style=\"color: #336666;\">true<\/span>;     <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"res3: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> res3 <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n              \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n              \n}\n<\/pre>\n<\/div>\n<p><span style=\"font-family: courier new, courier;\">MyInt<\/span> supports basic arithmetic with objects of type <span style=\"font-family: courier new, courier;\">MyInt<\/span> (1) but not basic arithmetic with built-in types such as <span style=\"font-family: courier new, courier;\">int <\/span>(2), <span style=\"font-family: courier new, courier;\">double<\/span>, or <span style=\"font-family: courier new, courier;\">bool<\/span> (3). The error message of the compiler gives an unambiguous message:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5929\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonExplicit.png\" alt=\"threeWayComparisonExplicit\" width=\"650\" height=\"159\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonExplicit.png 935w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonExplicit-300x73.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonExplicit-768x188.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>The compiler knows in (2) no conversion from <span style=\"font-family: courier new, courier;\">int<\/span> to <span style=\"font-family: courier new, courier;\">const MyInt<\/span> and in (3) no conversion form from <span style=\"font-family: courier new, courier;\">bool<\/span> to <span style=\"font-family: courier new, courier;\">const MyInt<\/span>. A viable way to make an <span style=\"font-family: courier new, courier;\">int<\/span>, <span style=\"font-family: courier new, courier;\">double<\/span>, or <span style=\"font-family: courier new, courier;\">bool<\/span> to <span style=\"font-family: courier new, courier;\">const MyInt<\/span> is a non-explicit constructor. Consequently, when I remove the <span style=\"font-family: courier new, courier;\">explicit<\/span> keyword from the constructor (1), the implicit conversion kicks in, and the program compiles and produces the surprising result.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5930\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/06\/threeWayComparisonImplicit.png\" alt=\"threeWayComparisonImplicit\" width=\"250\" height=\"157\" \/><\/p>\n<p>The compiler-generated == and != operators are special for performance reasons.<\/p>\n<h2>Optimized == and != operators<\/h2>\n<p>I wrote in my first post, &#8220;<a href=\"https:\/\/bit.ly\/ThreeWayComparison\">C++20: The Three-Way Comparison Operator<\/a>&#8220;, that the compiler-generated comparison operators apply lexicographical comparison. Lexicographical comparison means that all base classes are compared left to right and all non-static members in their declaration order.<\/p>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Andrew_Koenig_(programmer)\">Andrew Koenig<\/a> wrote a comment to my post &#8220;<a href=\"https:\/\/bit.ly\/MoreDetailsToSpaceship\">C++20: More Details to the Spaceship Operator<\/a>&#8221; on the Facebook group C++ Enthusiast, which I want to quote here:<\/p>\n<blockquote><p><span class=\"_3l3x _1n4g\">There\u2019s a potential performance problem with &lt;=&gt; that might be worth mentioning: for some types, it is often possible to implement == and != in a way that potentially runs much faster than &lt;=&gt;.<br \/>\nFor example, for a vectorlike or stringlike class, == and != can stop after determining that the two values being compared have different lengths, whereas &lt;=&gt; has to examine elements until it finds a difference. If one value is a prefix of the other, that makes the difference between O(1) and O(n).<\/span><\/p><\/blockquote>\n<p>I have nothing to add to Andrew&#8217;s comment but one observation. The standardization committee was aware of this performance issue and fixed it with the paper <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2019\/p1185r2.html\" target=\"_blank\" rel=\"noopener\">P1185R2.\u00a0<\/a> Consequently, the compiler-generated == and != operators compare in the case of a string or a vector first their length and then their content if necessary.<\/p>\n<h2>User-defined and auto-generated Comparison Operators<\/h2>\n<p>When you can define one of the six comparison operators and auto-generate all of them using the spaceship operator, there is one question: Which one has the higher priority? For example, my new implementation <span style=\"font-family: courier new, courier;\">MyInt<\/span> has a user-defined smaller and identity operator, and the compiler-generated six comparison operators.<\/p>\n<p>Let me see what happens:<\/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;\">\/\/ threeWayComparisonWithInt5.cpp<\/span>\n\n<span style=\"color: #009999;\">#include &lt;compare&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">MyInt<\/span> {\n <span style=\"color: #9999ff;\">public:<\/span>\n    constexpr <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} { }\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> {                  \n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"==  \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">==<\/span> rhs.value;\n    }\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> {                  \n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"&lt;  \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> value <span style=\"color: #555555;\">&lt;<\/span> rhs.value;\n    }\n    \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: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\n    \n <span style=\"color: #9999ff;\">private:<\/span>\n     <span style=\"color: #007788; font-weight: bold;\">int<\/span> value;\n};\n\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\n    \n    MyInt myInt2011(<span style=\"color: #ff6600;\">2011<\/span>);\n    MyInt myInt2014(<span style=\"color: #ff6600;\">2014<\/span>);\n    \n    myInt2011 <span style=\"color: #555555;\">==<\/span> myInt2014;\n    myInt2011 <span style=\"color: #555555;\">!=<\/span> myInt2014;\n    myInt2011 <span style=\"color: #555555;\">&lt;<\/span> myInt2014;\n    myInt2011 <span style=\"color: #555555;\">&lt;=<\/span> myInt2014;\n    myInt2011 <span style=\"color: #555555;\">&gt;<\/span> myInt2014;\n    myInt2011 <span style=\"color: #555555;\">&gt;=<\/span> myInt2014;\n    \n}\n<\/pre>\n<\/div>\n<p>To see the user-defined <span style=\"font-family: courier new, courier;\">==<\/span> and <span style=\"font-family: courier new, courier;\">&lt;<\/span> operators in action,\u00a0 I write a corresponding message to <span style=\"font-family: courier new, courier;\">std::cout<\/span>. Both operators cannot be <span style=\"font-family: courier new, courier;\">constexpr<\/span> because<span style=\"font-family: courier new, courier;\"> std::cout<\/span> is a run-time operation.<\/p>\n<p>The compiler uses the user-defined == and &lt; operators in this case. Additionally, the compiler synthesizes the <span style=\"font-family: courier new, courier;\">!=<\/span> operator out of the <span style=\"font-family: courier new, courier;\">==<\/span> operator. The compiler does not synthesize the == operator out of the other.<\/p>\n<p>This behavior does not surprise me because C++ behaves similarly to Python. In Python 3, the compiler generates != out of == if necessary but not the other way around. In Python 2, the so-called rich comparison (the user-defined six comparison operators) has higher priority than Python&#8217;s three-way comparison operator <span style=\"font-family: courier new, courier;\">__cmp__<\/span>. I have to say Python 2 because the three-way comparison operator is removed in Python 3.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>Designated initialization is a particular case of aggregate initialization and empowers you to initialize the members of a class using their names directly. Designed initializers are my next C++20 topic.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, I conclude my miniseries on the three-way comparison operator with a few subtle details. The subtle details include the compiler-generated == and != operators and the interplay of classical comparison operators, and the three-way comparison operator.<\/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-5931","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\/5931","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=5931"}],"version-history":[{"count":2,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5931\/revisions"}],"predecessor-version":[{"id":8756,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5931\/revisions\/8756"}],"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=5931"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5931"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5931"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}