<h1 id="i-did-not-order-this-why-is-it-on-my-bill-">I did not order this! Why is it on my bill?</h1>
<pre>
Document Number: P1190R0
Date: 2018-08-06
Author: David Stone (&#100;&#97;&#118;&#105;&#100;&#109;&#115;&#116;&#111;&#110;&#101;&#64;&#103;&#111;&#111;&#103;&#108;&#101;&#46;&#99;&#111;&#109;, &#100;&#97;&#118;&#105;&#100;&#64;&#100;&#111;&#117;&#98;&#108;&#101;&#119;&#105;&#115;&#101;&#46;&#110;&#101;&#116;)
Audience: LEWG, EWG
</pre>

<h2 id="purpose">Purpose</h2>
<p>There are many types in the standard library that have comparison operators that defer to the comparison operators of some template parameter (for instance, <code>vector</code>, <code>tuple</code>, and <code>variant</code>, henceforth &quot;wrappers&quot;). This paper explores the issues surrounding <code>operator&lt;=&gt;</code> for such wrappers. In particular, for some types, there is an important reason to define <code>operator==</code> and <code>operator!=</code> even in a world with <code>operator&lt;=&gt;</code>: it is sometimes cheaper to determine that two values are not equal than it is to determine in which way they are unequal. The canonical example here is differently-sized containers. Comparing their sizes is cheap (single integer comparison that does not need to follow a pointer), but comparing a value could be arbitrarily expensive. The current behavior of containers like <code>vector</code> is that no comparisons are performed on the contained objects if the sizes are not equal. It would be an unfortunate pessimization if <code>operator==</code> on a <code>vector&lt;int&gt;</code>, <code>vector&lt;vector&lt;int&gt;&gt;</code>, <code>tuple&lt;vector&lt;int&gt;&gt;</code>, or <code>struct { vector&lt;int&gt;; }</code> suddenly got slower in C++20. This paper explores several potential solutions to decide how to specify <code>operator&lt;=&gt;</code> for wrapper types.</p>
<h2 id="prior-work">Prior work</h2>
<ul>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4126.htm">N4126: &quot;Defaulted comparison operators&quot;</a>, Oleg Smolsky: This paper proposed supporting <code>= default</code> on all comparison operators and had no rules for implicit generation. It got wide support, but was eventually withdrawn in favor of <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0221r2.html">P0221R2</a>. The primary objection to this paper was the verbosity of enabling comparisons.</li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0221r2.html">P0221: &quot;Default comparisons&quot;</a>, Bjarne Stroustrup: This paper proposed generating all comparison operators by default if certain conditions were met. This made it to a full committee vote in plenary, during which it was rejected. The primary objections to this paper were that <code>operator&lt;</code> should not be generated by default, certain relational operators were not as performant as they could be, and there was no way to opt-out of the behavior.</li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0481r0.pdf">P0481: &quot;Bravely Default&quot;</a>, Tony Van Eerd: This paper proposed generating only <code>operator==</code> and <code>operator!=</code> by default if the copy constructor is implicitly generated, with explicit syntax of <code>= default</code> and <code>= delete</code> supported when necessary. This paper got a reasonable level of support in terms of the direction proposed. Ultimately, we abandoned this approach in favor of <code>operator&lt;=&gt;</code>. The primary objection to this paper was that it did not attempt to completely solve the comparison problem, it solved it only for equality.</li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0432r0.html">P0432: &quot;Implicit and Explicit Default Comparison Operators&quot;</a>, David Stone: This paper proposed much of the same mechanisms as <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0481r0.pdf">P0481R0</a>, but it also supported <code>= default</code> for relational operators (<code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, and <code>&gt;=</code>). &quot;Something kind of like this&quot; was well supported. This paper was withdrawn in favor of <code>operator&lt;=&gt;</code>. The primary objections to this paper were that it did not help with the code duplication between comparison operators and it led to less efficient relational operators.</li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0515r3.pdf">P0515: &quot;Consistent comparison&quot; (<code>operator&lt;=&gt;</code>)</a>, Herb Sutter: This paper was a synthesis of many of the previously mentioned paper (plus several others), in addition to new material. This paper was voted into the standard.</li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0768r1.pdf">P0768: &quot;Library Support for the Spaceship (Comparison) Operator&quot;</a>, Walter E. Brown: This is the companion paper to <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0515r3.pdf">P0515</a> and describes the additions to the standard library needed to support <code>operator&lt;=&gt;</code>.</li>
<li><a href="http://wg21.link/p0790">P0790: &quot;Effect of operator&lt;=&gt; on the standard library&quot;</a>, David Stone: This paper discusses simple additions of <code>operator&lt;=&gt;</code> to the standard library.</li>
</ul>
<h2 id="what-do-we-value-">What do we value?</h2>
<ul>
<li><strong>Performance</strong>: &quot;You don&#39;t pay for what you don&#39;t use&quot;. This is the guiding principle of C++. The abstractions provided by the language should be at least as efficient as those that a programmer could code manually. We should not impose unnecessary overhead.</li>
<li><strong>Maintainability</strong>: &quot;Don&#39;t repeat yourself&quot;. We want to specify a particular behavior in exactly one place.</li>
<li><strong>Respect the programmer&#39;s time</strong>: &quot;Make easy things easy&quot;. If the language can make a decision that is always (or almost always) correct, then we should not have to do it manually. If there is only one way to implement a feature, we should not require code to say <em>how</em> to do it.</li>
<li><strong>Respect the programmer&#39;s choices</strong>: &quot;Make hard things possible&quot;. Conversely, if there are many trade-offs involved and we cannot make one decision to fit everyone, we should refrain from making that decision for everyone.</li>
<li><strong>Backward compatibility</strong>: The more existing code we break, the stronger justification we need. It is better to cause existing code to fail to compile rather than silently change behavior.</li>
</ul>
<h2 id="the-problems">The Problems</h2>
<h3 id="performance">Performance</h3>
<p>The obvious specifications for <code>operator&lt;=&gt;</code> on variable-sized containers is significantly slower than <code>operator==</code> is today because <code>operator&lt;=&gt;</code> cannot short circuit based on size (you know the two containers are unequal, but you do not know which is less), but <code>operator==</code> can. This ends up having far reaching consequences.</p>
<h3 id="backward-compatibility">Backward compatibility</h3>
<p>When we add <code>operator&lt;=&gt;</code> to wrapper types, what do we do when the wrappers are given types that do not implement <code>operator&lt;=&gt;</code>? If we define the wrapper&#39;s <code>operator&lt;=&gt;</code> to call the wrapped type&#39;s <code>operator&lt;=&gt;</code>, we do not generate <code>operator&lt;=&gt;</code> for such a type, and if we unconditionally remove <code>operator==</code>, <code>operator!=</code>, <code>operator&lt;</code>, <code>operator&lt;=</code>, <code>operator&gt;</code>, and <code>operator&gt;=</code> from the wrapper types, the wrappers suddenly become non-comparable when instantiated with such types. If we unconditionally use <code>compare_3way</code> to implement these types so that we fall back on <code>operator==</code> and <code>operator&lt;</code> for the underlying type, we end up with a silent performance change that makes such types up to twice as expensive to compare.</p>
<h2 id="background">Background</h2>
<p>Prior to <code>operator&lt;=&gt;</code>, how do you implement <code>operator==</code> for a <code>SequenceContainer</code>, such as <code>vector</code>? (note: this paper intentially inlines the call to algorithms like <code>equal</code> and <code>lexicographical_compare</code>, and names the functions something other than <code>operator==</code> to give them a name for discussion)</p>
<pre><code><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">equal1</span><span class="hljs-params">(<span class="hljs-built_in">vector</span>&lt;T&gt; <span class="hljs-keyword">const</span> &amp; lhs, <span class="hljs-built_in">vector</span>&lt;T&gt; <span class="hljs-keyword">const</span> &amp; rhs)</span> </span>{
    <span class="hljs-keyword">if</span> (lhs.size() != rhs.size()) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">auto</span> lhs_first = lhs.begin();
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> lhs_last = lhs.end();
    <span class="hljs-keyword">auto</span> rhs_first = rhs.begin();
    <span class="hljs-keyword">while</span> (lhs_first != lhs_last) {
        <span class="hljs-keyword">if</span> (!(*lhs_first == *rhs_first)) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
        ++lhs_first;
        ++rhs_first;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre><p>This ensures that two differently-sized containers will immediately return false. It performs a single comparison of a data member within the <code>vector</code>, then each iteration performs a single iterator comparison. It is important to note that the early size check lets us avoid following the internal data pointer in the container and lets us avoid a loop / function call. If we try to implement this without having the size check (note that it is currently mandated that implementations perform this size check), we have two possible solutions:</p>
<pre><code><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">bool</span> equal2(vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; lhs, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; rhs) {
    <span class="hljs-keyword">auto</span> lhs_first = lhs.<span class="hljs-built_in">begin</span>();
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> lhs_last = lhs.<span class="hljs-built_in">end</span>();
    <span class="hljs-keyword">auto</span> rhs_first = rhs.<span class="hljs-built_in">begin</span>();
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> rhs_last = rhs.<span class="hljs-built_in">end</span>();
    <span class="hljs-built_in">while</span> (lhs_first != lhs_last &amp;&amp; rhs_first != rhs_last) {
        <span class="hljs-built_in">if</span> (!(*lhs_first == *rhs_first)) {
            <span class="hljs-built_in">return</span> false;
        }
        ++lhs_first;
        ++rhs_first;
    }
    <span class="hljs-built_in">return</span> lhs.<span class="hljs-built_in">size</span>() == rhs.<span class="hljs-built_in">size</span>();
}

<span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">bool</span> equal3(vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; lhs, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; rhs) {
    <span class="hljs-keyword">auto</span> impl = [](vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; smaller, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; larger) {
        <span class="hljs-keyword">auto</span> smaller_first = smaller.<span class="hljs-built_in">begin</span>();
        <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> smaller_last = smaller.<span class="hljs-built_in">end</span>();
        <span class="hljs-keyword">auto</span> larger_first = larger.<span class="hljs-built_in">begin</span>();
        <span class="hljs-built_in">while</span> (smaller_first != smaller_last) {
            <span class="hljs-built_in">if</span> (!(*smaller_first == *larger_first)) {
                <span class="hljs-built_in">return</span> false;
            }
            ++smaller_first;
            ++larger_first;
        }
        <span class="hljs-built_in">return</span> larger_first == larger.<span class="hljs-built_in">end</span>();
    };

    <span class="hljs-built_in">return</span> lhs.<span class="hljs-built_in">size</span>() &lt;= rhs.<span class="hljs-built_in">size</span>() ? impl(lhs, rhs) : impl(rhs, lhs);
}
</code></pre><p>You can see the <a href="https://godbolt.org/z/17lOir">generated code for these versions on Godbolt</a>.</p>
<p><code>equal2</code> is the straightforward approach, which ignores the problem. It means that for each element in the smaller container, we have two iterator comparisons instead of just one. For the common case of <code>vector&lt;Integer&gt;</code> or <code>string</code>, this changes our per iteration cost from two increments and two compare + branch to two increments, one compare + branch, and one compare + compare + and + branch. Using a very rough estimation, this adds an extra two operations per loop.</p>
<p><code>equal3</code> tries to be smarter. It is optimized for longer sequences, because it pays a small fixed cost for the whole comparison to keep the loop costs the same as the first version. Something much like this is essentially what gcc&#39;s libstdc++ did for comparing <code>string</code> with <code>char const *</code>. Compared with <code>equal1</code>, this was benchmarked to dramatically increase the costs of comparisons (depending on the benchmark, anywhere from 0 to 66% of the total cost was removed when moving from something like <code>equal3</code> to something like <code>equal1</code>, but with <code>strlen</code> and <code>memcmp</code> mixed in).</p>
<p>Neither of these two options are satisfying. They all have at least as much in fixed costs as <code>equal1</code>, and <code>equal2</code> has much more in per-loop costs. Unfortunately, straightforward application of <code>operator&lt;=&gt;</code> to the containers gives us a generated <code>operator==</code> that is the moral equivalent to <code>equal2</code> or <code>equal3</code>.</p>
<h3 id="order-order-in-the-container-">Order! Order in the container!</h3>
<p><code>operator&lt;=&gt;</code> for containers must support ordering containers if the contained type can be ordered. If we want to maintain current ordering (more on that later), <code>operator&lt;=&gt;</code> would look something one of these two solutions:</p>
<pre><code><span class="hljs-comment">// Does something like `equal2` above</span>
<span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">auto</span> compare2(vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; lhs, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; rhs) {
    <span class="hljs-keyword">auto</span> lhs_first = lhs.<span class="hljs-built_in">begin</span>();
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> lhs_last = lhs.<span class="hljs-built_in">end</span>();
    <span class="hljs-keyword">auto</span> rhs_first = rhs.<span class="hljs-built_in">begin</span>();
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> rhs_last = rhs.<span class="hljs-built_in">end</span>();
    <span class="hljs-built_in">while</span> (lhs_first != lhs_last &amp;&amp; rhs_first != rhs_last) {
        <span class="hljs-built_in">if</span> (<span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> cmp = compare_3way(*lhs_first, *rhs_first); cmp != <span class="hljs-number">0</span>) {
            <span class="hljs-built_in">return</span> cmp;
        }
        ++lhs_first;
        ++rhs_first;
    }
    <span class="hljs-built_in">return</span> lhs.<span class="hljs-built_in">size</span>() &lt;=&gt; rhs.<span class="hljs-built_in">size</span>();
}

<span class="hljs-comment">// Does something like `equal3` above</span>
<span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">auto</span> compare3(vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; lhs, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; rhs) {
    <span class="hljs-keyword">auto</span> impl = [](vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; smaller, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; larger) {
        <span class="hljs-keyword">auto</span> smaller_first = smaller.<span class="hljs-built_in">begin</span>();
        <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> smaller_last = smaller.<span class="hljs-built_in">end</span>();
        <span class="hljs-keyword">auto</span> larger_first = larger.<span class="hljs-built_in">begin</span>();
        <span class="hljs-built_in">while</span> (smaller_first != smaller_last) {
            <span class="hljs-built_in">if</span> (<span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> cmp = compare_3way(*smaller_first, *larger_first); cmp != <span class="hljs-number">0</span>) {
                <span class="hljs-built_in">return</span> cmp;
            }
            ++smaller_first;
            ++larger_first;
        }
        <span class="hljs-built_in">return</span> larger_first == larger.<span class="hljs-built_in">end</span>() ? strong_ordering::equal : strong_ordering::less;
    };
    <span class="hljs-keyword">auto</span> reverse_ordering = [](<span class="hljs-keyword">auto</span> order) {
        <span class="hljs-built_in">return</span> <span class="hljs-number">0</span> &lt;=&gt; order;
    };

    <span class="hljs-built_in">return</span> lhs.<span class="hljs-built_in">size</span>() &lt;= rhs.<span class="hljs-built_in">size</span>() ? impl(lhs, rhs) : reverse_ordering(impl(rhs, lhs));
}
</code></pre><h2 id="scope-of-the-problem">Scope of the problem</h2>
<h3 id="ranges-that-are-affected-by-this-problem">Ranges that are affected by this problem</h3>
<p>These types are all variable-size, sized, ordered ranges, which are all directly affected by this problem.</p>
<ul>
<li><code>basic_string</code></li>
<li><code>basic_string_view</code></li>
<li><code>deque</code></li>
<li><code>list</code></li>
<li><code>vector</code> (including <code>vector&lt;bool&gt;</code>)</li>
<li><code>map</code></li>
<li><code>set</code></li>
<li><code>multimap</code></li>
<li><code>multiset</code></li>
<li><code>queue</code></li>
<li><code>stack</code></li>
</ul>
<h3 id="types-that-wrap-another-type-and-thus-could-stumble-onto-this-problem-if-we-do-nothing">Types that wrap another type, and thus could stumble onto this problem if we do nothing</h3>
<ul>
<li><code>array</code></li>
<li><code>pair</code></li>
<li><code>tuple</code></li>
<li><code>reverse_iterator</code></li>
<li><code>move_iterator</code></li>
<li><code>optional</code>, including heterogeneous <code>optional&lt;T&gt; &lt;=&gt; optional&lt;U&gt;</code>, <code>optional&lt;T&gt; &lt;=&gt; U</code>, <code>optional&lt;T&gt; &lt;=&gt; nullopt_t</code>, and <code>nullopt_t &lt;=&gt; nullopt_t</code></li>
<li><code>variant</code></li>
<li><code>forward_list</code></li>
</ul>
<p>All of these are either fixed-size (in many cases, size == 1) or, in the case of <code>forward_list</code>, unsized, and thus would not benefit from an early exit based on different sizes. If we can define a maximally efficient <code>operator&lt;=&gt;</code> for all types, these types don&#39;t need to do anything special with regards to performance (<code>operator&lt;=&gt; = default</code> would be sufficient).</p>
<h3 id="containers-that-are-not-affected-by-this-performance-problem">Containers that are not affected by this performance problem</h3>
<ul>
<li><code>unordered_map</code></li>
<li><code>unordered_set</code></li>
<li><code>unordered_multimap</code></li>
<li><code>unordered_multiset</code></li>
</ul>
<p>The containers on this list provide only <code>operator==</code> and <code>operator!=</code>. Their <code>operator&lt;=&gt;</code> will return, at best, <code>strong_equality</code>. As long as a valid implementation of <code>operator&lt;=&gt;</code> is to call <code>a == b</code> for each element, these containers are unaffected by the performance problem outlined in this paper.</p>
<h2 id="possible-solutions-for-the-performance-problem">Possible solutions for the performance problem</h2>
<h3 id="define-operator-and-operator-for-ranges">Define <code>operator==</code> and <code>operator!=</code> for ranges</h3>
<p>This is a tempting solution. The author of the range type merely defines their own <code>operator==</code> (and <code>operator!=</code> in terms of it) that does the size short circuiting. Unfortunately, this solution just pushes the problem up. If we define <code>operator==</code> and <code>operator!=</code> for ranges, we run into the same problem with the obvious specification of <code>operator&lt;=&gt;</code> for types like <code>tuple</code>, so now any type that calls another type&#39;s <code>operator&lt;=&gt;</code> also has to define its own <code>operator==</code> and <code>operator!=</code>. Even if we specify types like <code>tuple</code> to have all three of <code>operator&lt;=&gt;</code>, <code>operator==</code>, and <code>operator!=</code>, current wording for <code>operator&lt;=&gt;</code> would make user-defined types like</p>
<pre><code><span class="hljs-keyword">struct</span> S {
    <span class="hljs-built_in">string</span> str;
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">operator</span>&lt;=&gt;(S <span class="hljs-keyword">const</span> &amp;) <span class="hljs-keyword">const</span> = <span class="hljs-keyword">default</span>;
};
</code></pre><p>slower than <code>string</code>. If we accept this library-only solution, we must sacrifice maintainability and respecting the programmer&#39;s time.</p>
<h3 id="define-operator-and-operator-for-ranges-and-synthesize-those-operators-in-general">Define <code>operator==</code> and <code>operator!=</code> for ranges, and synthesize those operators in general</h3>
<p>This takes the previous approach, but does not simply define defaulted <code>operator==</code> and <code>operator!=</code> in terms of <code>operator&lt;=&gt;</code>. Instead, we create a slightly more complicated set of rules to pick up user-defined <code>operator==</code>.</p>
<ul>
<li>For the expression <code>a != b</code>, call <code>!(a == b)</code> instead of <code>a &lt;=&gt; b != 0</code>. For types that define only <code>operator&lt;=&gt;</code> this has the same effect as current rules.</li>
<li>When the user defines <code>auto operator&lt;=&gt;(T const &amp;) const = default;</code>, this gives a memberwise <code>operator&lt;=&gt;</code> and a memberwise <code>operator==</code>. In other words, rather than creating a rewrite rule for <code>operator==</code>, we define an actual operator. For types that contain only data members that do not have an explicitly-defined <code>operator==</code>, this has the same effect as the current rules. If, however, some of the data members do have an explicitly-defined <code>operator==</code>, this will ensure that the generated <code>operator==</code> and <code>operator!=</code> for the wrapper respect the definition of each contained type. This will ensure that <code>tuple&lt;string&gt;</code> and the <code>struct S</code> example above naturally are as efficient as possible when compared for equality.</li>
</ul>
<p>There are three main issues with this approach:</p>
<ul>
<li>Increased code size. Rather than having one function defined that all other operators are rewritten to be in terms of, we have two. In practice, this would be necessary only in those cases where the user has defined <code>operator==</code> (presumably because it was necessary), so this is an example of paying for what you do use.</li>
<li>Defaulting <code>operator&lt;=&gt;</code> is also defaulting <code>operator==</code>. It&#39;s a bit strange that a user would have to define both <code>operator&lt;=&gt;</code> and <code>operator==</code> to get the behavior they would get from <code>auto operator&lt;=&gt;(...) const = default</code>. We could make this behavior more explicit and require users to type <code>bool operator==(T const &amp;) const = default</code> to opt-in to this behavior, but I do not recommend this approach because forgetting to do so leads to a silent performance problem, rather than a compiler error. We could make it a compiler error to use <code>operator==</code> on a type that has <code>auto operator&lt;=&gt;(...) const = default</code> if any of the data members have an explicitly-defined <code>operator==</code>. At this point, we are making the user type more code to get the behavior that they almost certainly want (what would the error message from your compiler look like? &quot;You meant to put <code>bool operator==(T const &amp;) const = default;</code> somewhere in your class.&quot;). The model of <code>operator&lt;=&gt;</code> is already that it is rewriting calls to other operators, so from that perspective it might not be that strange.</li>
<li>We need to define when exactly we follow the rule of &quot;For the expression <code>a != b</code>, call <code>!(a == b)</code>&quot;. There are two options here: 1) If <code>a == b</code> is valid (changes code without <code>operator&lt;=&gt;</code>) or 2) Only when <code>operator&lt;=&gt;</code> is defined (we change the specification of <code>operator&lt;=&gt;</code> to rewrite <code>a != b</code> as <code>!(a == b)</code> instead of <code>a &lt;=&gt; b != 0</code>).</li>
</ul>
<h3 id="make-operator-create-only-operator-operator-operator-and-operator-">Make <code>operator&lt;=&gt;</code> create only <code>operator&lt;</code>, <code>operator&lt;=</code>, <code>operator&gt;</code>, and <code>operator&gt;=</code></h3>
<p>In other words, <code>operator&lt;=&gt;</code> would be for ordering only, not equality. If we take this approach, we would also want to allow <code>bool operator==(T const &amp;) const = default;</code> and <code>bool operator!=(T const &amp;) const = default;</code>. Where the above approach causes a silent performance issue when we try to make this behavior explicit, this approach simply does not give you equality unless you explicitly ask for it.</p>
<p>There are two main issues with this approach:</p>
<p>1) Compared to the previous solution, this requires the user to type even more to opt-in to behavior that they almost always want (if you have defaulted relational operators, you probably want the equality operators). Because <code>operator&lt;=&gt;</code> is a new feature, we do not have any concerns of legacy code, so if the feature starts out as giving users all six comparison operators, it would be better if they must type only one line rather than having to type three.
2) It is a natural side-effect of computing less than vs. greater than that you compute equal to. It is strange that we define an operator that can tell us whether things are equal, but we use it to generate all comparisons other than equal and not equal. For the large set of types for which <code>operator&lt;=&gt;</code> alone is sufficient, it also means that users who are not using the default (they are explicitly defining the comparisons) must define two operators that encode much of the same logic of comparison. This mandatory duplication invites bugs as the code is changed under maintenance.</p>
<h3 id="add-a-new-comparison-category-that-creates-only-operator-operator-operator-and-operator-">Add a new comparison category that creates only <code>operator&lt;</code>, <code>operator&lt;=</code>, <code>operator&gt;</code>, and <code>operator&gt;=</code></h3>
<p>An interesting modification to this idea would be to create one more comparison category that, when used as the return type of <code>operator&lt;=&gt;</code>, leads to only the relational operators being synthesized. When calling <code>operator&lt;=&gt;</code> directly, there would still be a value that tells whether the two objects are equal (as this is a natural side-effect), it would be only the generation that is affected.</p>
<p>This has most of the same drawbacks as the previous approach, except that it allows the user to say &quot;No, this type is different, <code>operator&lt;=&gt;</code> is not good enough for <code>operator==</code> and <code>operator!=</code>&quot;.</p>
<h3 id="pascalexicographical-order">Pascalexicographical order</h3>
<p>The previous solutions accept that <code>operator&lt;=&gt;</code> is slower than <code>operator==</code> and provide ways to have both. The ideal solution would make <code>lhs &lt;=&gt; rhs == 0</code> be just as fast as <code>lhs == rhs</code>, and have <code>lhs == rhs</code> be just as fast in C++20 as it is in C++17. We can have such a solution if we are willing to sacrifice either backward compatibility or consistency. In C++17 and earlier, <code>operator&lt;</code> gives a lexicographical order for containers, which is an extension of alphabetical order. It essentially sorts first by elements, then by size. &quot;Pascalexicographical order&quot; sorts first by size, then by elements.</p>
<table>
<thead>
<tr>
<th>Lexicographical (what we currently have)</th>
<th>Pascalexicographical</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&quot;a&quot;s == &quot;a&quot;s</code></td>
<td><code>&quot;a&quot;s == &quot;a&quot;s</code></td>
</tr>
<tr>
<td><code>&quot;a&quot;s &lt; &quot;z&quot;s</code></td>
<td><code>&quot;a&quot;s &lt; &quot;z&quot;s</code></td>
</tr>
<tr>
<td><code>&quot;a&quot;s &lt; &quot;aa&quot;s</code></td>
<td><code>&quot;a&quot;s &lt; &quot;aa&quot;s</code></td>
</tr>
<tr>
<td><code>&quot;aa&quot;s &lt; &quot;z&quot;s</code></td>
<td><code>&quot;z&quot;s &lt; &quot;aa&quot;s</code></td>
</tr>
</tbody>
</table>
<p>What does an <code>operator&lt;=&gt;</code> that gives this ordering look like?</p>
<pre><code><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">auto</span> compare1(vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; lhs, vector&lt;T&gt; <span class="hljs-keyword">const</span> &amp; rhs) {
    <span class="hljs-built_in">if</span> (<span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> cmp = lhs.<span class="hljs-built_in">size</span>() &lt;=&gt; rhs.<span class="hljs-built_in">size</span>(); cmp != <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">return</span> cmp;
    }
    <span class="hljs-keyword">auto</span> lhs_first = lhs.<span class="hljs-built_in">begin</span>();
    <span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> lhs_last = lhs.<span class="hljs-built_in">end</span>();
    <span class="hljs-keyword">auto</span> rhs_first = rhs.<span class="hljs-built_in">begin</span>();
    <span class="hljs-built_in">while</span> (lhs_first != lhs_last) {
        <span class="hljs-built_in">if</span> (<span class="hljs-keyword">auto</span> <span class="hljs-keyword">const</span> cmp = compare_3way(*lhs_first, *rhs_first); cmp != <span class="hljs-number">0</span>) {
            <span class="hljs-built_in">return</span> cmp;
        }
        ++lhs_first;
        ++rhs_first;
    }
    <span class="hljs-built_in">return</span> strong_ordering_equal;
}
</code></pre><p>Now we are back to essentially the same fast code that we had for <code>equal1</code>.</p>
<p>This operator fully embraces the idea that for many uses, &quot;any order&quot; is sufficient, so it provides the fastest strong ordering possible. There are certainly users that depend on the order of these wrappers (that is to say, they need the specific order given by a lexicographical comparison, they do not just need any ordering). This means that the straightforward application of this change would be a major, silent breaking change. But is there some clever way we can do this such that it does not break any users?</p>
<p>One thought could be to define <code>operator&lt;=&gt;</code> with this new order only for those types that, themselves, define <code>operator&lt;=&gt;</code>. We know that no user code is doing this today, so no existing code will change behavior. This immediately runs into one major stumbling block, the foremost example of which is <code>basic_string&lt;char&gt;</code>. <code>char</code> has <code>operator&lt;=&gt;</code>, and changing the default sorting order of strings is likely the biggest breaking change in this category. If we try to exclude <code>basic_string&lt;char&gt;</code> from this change in ordering, we also lose out on most of the performance benefits.</p>
<p>The other option would be to make this change for <code>operator&lt;=&gt;</code> only. The long-term plan could be for <code>operator==</code> and <code>operator!=</code> to not be defined explicitly for these wrapper types (they just defer to <code>operator&lt;=&gt;</code>), but the relational operators remain and provide a lexicographical ordering. This maintains backward compatibility and ensures that we generate code that is fast by default, but has a huge loss in consistency (for all of the most common standard library types, <code>a &lt;=&gt; b &lt; 0</code> is not the same as <code>a &lt; b</code>, despite that being the rewrite rule).</p>
<p>In theory, <a href="http://www.open-std.org/mailman/listinfo/tooling">there could exist a tool</a> that (as part of the C++20 upgrade process) rewrote all existing uses of relational operators like <code>operator&lt;</code> to be <code>lexicographical_compare</code> for affected containers...</p>
<h2 id="how-to-specify-wrapper-types-other-than-the-performance-problem-">How to specify wrapper types (other than the performance problem)</h2>
<p>Any solution we decide on (other than the non-solution of not providing <code>operator&lt;=&gt;</code> for any of these types) will have at least one thing in common: the return type will be the common comparison result type for comparing each of the elements, similar to <code>lexicographical_compare_3way</code>.</p>
<p>When given a type that does not have <code>operator&lt;=&gt;</code>, there are two distinct possibilities: either we provide <code>operator&lt;=&gt;</code> or we do not. The standard library has <code>compare_3way</code>, which calls <code>operator&lt;=&gt;</code> if possible, otherwise it synthesizes it from <code>operator==</code> and <code>operator&lt;</code>. If we require all calls in wrapper types to go through <code>compare_3way</code> rather than <code>operator&lt;=&gt;</code> on all wrapped types, we get <code>operator&lt;=&gt;</code> automatically for free. This would also work with the default <code>lexicographical_compare_3way</code> as an implementation strategy. It seems better to always provide <code>operator&lt;=&gt;</code> to give a consistent user experience (&quot;<code>vector&lt;T&gt;</code> always has <code>operator&lt;=&gt;</code>&quot;) and it can give better performance in situations that require 3-way comparisons -- in the case of <code>vector&lt;vector&lt;T&gt;&gt;</code>, for instance, doing all necessary comparisons on the first <code>vector</code> element, even if that implementation is synthesized, means that you iterate over the outer vector only once, which is slightly less traffic on your memory bus.</p>
<p>However, we would not want to remove all of the old comparison operators for types that do not define <code>operator&lt;=&gt;</code>, otherwise we would have a silent performance degredation from C++17 to C++20. Here, we have a choice again. We can define the 6 comparison operators only for those types that do not define <code>operator&lt;=&gt;</code>, or we can define them always. Here, it probably makes sense to define them only for types that do not have <code>operator&lt;=&gt;</code>, unless we go for a solution to the performance problem that involves generating <code>operator==</code> and <code>operator!=</code>, in which case we should define those as well.</p>
<h2 id="summary-suggested-polls">Summary / Suggested polls</h2>
<ul>
<li>Synthesize a memberwise <code>operator==</code> when the user defaults <code>operator&lt;=&gt;</code></li>
<li>Synthesize <code>operator!=</code> as being rewritten to <code>!(lhs == rhs)</code> (following the same precedence / name hiding rules as <code>operator&lt;=&gt;</code>) for all types</li>
<li>Synthesize <code>operator!=</code> as being rewritten to <code>!(lhs == rhs)</code> (following the same precedence / name hiding rules as <code>operator&lt;=&gt;</code>) only when <code>operator&lt;=&gt;</code> is defined</li>
<li>Make <code>operator&lt;=&gt;</code> create only <code>operator&lt;</code>, <code>operator&lt;=</code>, <code>operator&gt;</code>, and <code>operator&gt;=</code>, not <code>operator==</code> or <code>operator!=</code></li>
<li>Add a new comparison category that creates only <code>operator&lt;</code>, <code>operator&lt;=</code>, <code>operator&gt;</code>, and <code>operator&gt;=</code>, not <code>operator==</code> or <code>operator!=</code></li>
<li>Change the way all containers are ordered with all comparison operators to be implementation-defined</li>
<li>Change the way all containers are ordered with all comparison operators to be &quot;Pascalexicographical&quot;, thus not comparing any contained elements if the sizes are unequal</li>
<li>Change the way all containers are ordered with <code>operator&lt;=&gt;</code> only to be implementation-defined</li>
<li>Change the way all containers are ordered with <code>operator&lt;=&gt;</code> only to be &quot;Pascalexicographical&quot;, thus not comparing any contained elements if the sizes are unequal</li>
<li>Wrapper types should always have <code>operator&lt;=&gt;</code>, even when given a type that does not have <code>operator&lt;=&gt;</code></li>
<li>Deprecate the comparison operators from wrapper types if the wrapped type has <code>operator&lt;=&gt;</code></li>
<li>Remove the comparison operators from wrapper types if the wrapped type has <code>operator&lt;=&gt;</code></li>
</ul>

