<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2852: Specifications of operator== for std::basic_strings and std::basic_string_views are
difficult to conform to</title>
<meta property="og:title" content="Issue 2852: Specifications of operator== for std::basic_strings and std::basic_string_views are
difficult to conform to">
<meta property="og:description" content="C++ library issue. Status: NAD">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2852.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#NAD">NAD</a> status.</em></p>
<h3 id="2852"><a href="lwg-closed.html#2852">2852</a>. Specifications of <code>operator==</code> for <code>std::basic_string</code>s and <code>std::basic_string_view</code>s are
difficult to conform to</h3>
<p><b>Section:</b> 27.4.4.2 <a href="https://wg21.link/string.cmp">[string.cmp]</a>, 27.4.3.8.4 <a href="https://wg21.link/string.compare">[string.compare]</a> <b>Status:</b> <a href="lwg-active.html#NAD">NAD</a>
 <b>Submitter:</b> Ahti Lepp&auml;nen <b>Opened:</b> 2017-01-09 <b>Last modified:</b> 2021-06-06</p>
<p><b>Priority: </b>2
</p>
<p><b>View all issues with</b> <a href="lwg-status.html#NAD">NAD</a> status.</p>
<p><b>Discussion:</b></p>
<p>
Currently (<a href="https://wg21.link/n4618">N4618</a>, 2016-11-28) the specification of <code>operator==</code>
for <code>std::basic_string</code> and <code>std::basic_string_view</code> objects is clearly defined, but when
interpreted as written, it may lead to comparison of strings of different sizes being a &#x1d4aa;(n) operation
instead of a simple size check. Actual implementations in standard libraries vary so that in practice the
programmers can't rely neither on having the literal version of the standard specification nor reasonable
performance characteristics.
<p/>
The definition for <code>basic_string operator==</code> in N4618 is as follows:
</p>
<blockquote>
<p>
 [string.operator==]
</p>
<pre>
bool operator==(const basic_string&lt;charT, traits, Allocator&gt;&amp; lhs,
                const basic_string&lt;charT, traits, Allocator&gt;&amp; rhs) noexcept;
</pre>
<blockquote>
<p>
-1- <i>Returns:</i> <code>lhs.compare(rhs) == 0</code>.
</p>
</blockquote>
</blockquote>

<blockquote>
<p>
27.4.3.8.4 <a href="https://wg21.link/string.compare">[string.compare]</a>
</p>
<pre>
int compare(const basic_string&amp; str) const noexcept;
</pre>
<blockquote>
<p>
-6- <i>Effects:</i> Equivalent to: <code>return compare(basic_string_view&lt;charT, traits&gt;(str));</code>
</p>
</blockquote>
</blockquote>

<blockquote>
<p>
27.4.3.8.4 <a href="https://wg21.link/string.compare">[string.compare]</a>
</p>
<pre>
int compare(basic_string_view&lt;charT, traits&gt; sv) const noexcept;
</pre>
<blockquote>
<p>
-1- <i>Effects:</i> Determines the effective length <code>rlen</code> of the strings to compare as the smaller of
<code>size()</code> and <code>sv.size()</code>. The function then compares the two strings by calling <code>traits::compare(data(),
sv.data(), rlen)</code>.
<p/>
-2- <i>Returns:</i> The nonzero result if the result of the comparison is nonzero. Otherwise, returns a value as
indicated in Table 63.
</p>
</blockquote>
<blockquote>
<table border="1">
<caption>Table 63 &mdash; <code>compare()</code> results</caption>
<tr>
<th>Condition</th>
<th>Return Value</th>
</tr>

<tr>
<td><code>size() &lt; sv.size()</code></td>
<td><code>&lt; 0</code></td>
</tr>

<tr>
<td><code>size() == sv.size()</code></td>
<td><code>0</code></td>
</tr>

<tr>
<td><code>size() &gt; sv.size()</code></td>
<td><code>&gt; 0</code></td>
</tr>
</table>

</blockquote>
</blockquote>
<p>
From these it seems that <code>compare()</code> of strings of different sizes can't return zero and <code>operator==</code>
will return <code>false</code>. However some implementations do not seem to call <code>traits::compare()</code> for
<code>basic_string</code>s of different sizes even when the traits and it's <code>compare()</code> are user-defined. And those
that call, make the <code>operator==</code> a worst case &#x1d4aa;(n) operation even for strings of different sizes.
<p/>
This defect report does not propose a wording, but on a general level the wording should allow standard library
implementers to write a standard conforming <code>operator==</code> for <code>basic_string</code> and <code>basic_string_view</code>
(others?) in such a way that it's performance characteristics are reasonable and the programmers can rely on having
a consistent behaviour across implementations. Perhaps the key issue here is that <code>operator==</code> is defined
through <code>compare() == 0</code>: while it returns the intended result, for some inputs it does computations that
are not needed by <code>operator==</code>. There are also related specifications that may need to be revised, for example
<code>operator!=</code> for <code>basic_string_view</code>s is defined in 27.3.4 <a href="https://wg21.link/string.view.comparison">[string.view.comparison]</a> as
</p>
<blockquote>
<p>
<i>Returns:</i> <code>lhs.compare(rhs) != 0</code>
</p>
</blockquote>

<p><i>[2017-01-26, Jonathan Wakely comments and provides proposed resolution]</i></p>

<p>
As mentioned above, some implementations do not make a call to <code>Traits::compare</code> if the string lengths are not equal,
even though in general this is an observable side effect. Some implementations only perform that optimisation for
<code>std::string</code> and <code>std::wstring</code>, where we know that calls to <code>std::char_traits&lt;char&gt;::compare</code>
and <code>std::char_traits&lt;wchar_t&gt;::compare</code> are not observable.
<p/>
My reading is that the <i>Returns:</i> element describes the value that must be returned, not the precise steps that must be
taken to calculate that value. If we intended to specify the precise steps that must be taken then we could say that using
"<i>Effects:</i> Equivalent to [&hellip;]", but we don't do that.
<p/>
I would prefer this issue to be closed NAD with the rationale that my reading is correct and comparing the lengths to avoid
calling <code>Traits::compare</code> is already permitted. But if my reading is wrong we need to permit this obvious optimisation.
</p>

<p><i>[2017-01-27 Telecon]</i></p>

<p>Priority 2</p>

<p><i>[2017-02-04, Ahti Lepp&auml;nen comments and recommends NAD]</i></p>

<p>
While there seems to be varying interpretations of the standards wording, given the comments in this defect report and
definitions in  [structure.specification] (N4618):
</p>
<blockquote><p>
<i>Effects:</i> the actions performed by the function
<p/>
<i>Returns:</i> a description of the value(s) returned by the function
</p>
</blockquote>
<p>
I fail to see that the specification of <code>operator==</code> "<i>Returns:</i> <code>lhs.compare(rhs) == 0</code>" would require call
to <code>compare()</code> and no longer consider the report valid.
</p>

<p><i>[2016-07, Toronto Saturday afternoon issues processing]</i></p>

<p>Status to NAD; we accept Jonathan's reasoning. Note that several implementations do this today.</p>


<p id="res-2852"><b>Proposed resolution:</b></p>
<p>This wording is relative to <a href="https://wg21.link/n4618">N4618</a>.</p>

<ol style="list-style-type:upper-alpha">
<li><p>Preferred: NAD</p></li>
<li><p>Alternative:</p>

<ol>
<li><p>Modify  [string.operator==] p1 as shown:</p>
<blockquote>
<pre>
template&lt;class charT, class traits, class Allocator&gt;
  bool operator==(const basic_string&lt;charT, traits, Allocator&gt;&amp; lhs,
                  const basic_string&lt;charT, traits, Allocator&gt;&amp; rhs) noexcept;
</pre>
<blockquote>
<p>
-1- <i>Returns:</i> <code><ins>lhs.size() == rhs.size() &amp;&amp;</ins> lhs.compare(rhs) == 0</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Modify 27.3.4 <a href="https://wg21.link/string.view.comparison">[string.view.comparison]</a> as shown:</p>
<blockquote>
<p>
[<i>Example:</i> A sample conforming implementation for <code>operator==</code> would be:
</p>
<pre>
template&lt;class T&gt; using __identity = decay_t&lt;T&gt;;
template&lt;class charT, class traits&gt;
  constexpr bool operator==(basic_string_view&lt;charT, traits&gt; lhs,
                            basic_string_view&lt;charT, traits&gt; rhs) noexcept {
    return <ins>lhs.size() == rhs.size() &amp;&amp;</ins> lhs.compare(rhs) == 0;
  }
template&lt;class charT, class traits&gt;
  constexpr bool operator==(basic_string_view&lt;charT, traits&gt; lhs,
                            __identity&lt;basic_string_view&lt;charT, traits&gt;&gt; rhs) noexcept {
    return <ins>lhs.size() == rhs.size() &amp;&amp;</ins> lhs.compare(rhs) == 0;
  }
template&lt;class charT, class traits&gt;
  constexpr bool operator==(__identity&lt;basic_string_view&lt;charT, traits&gt;&gt; lhs,
                            basic_string_view&lt;charT, traits&gt; rhs) noexcept {
    return <ins>lhs.size() == rhs.size() &amp;&amp;</ins> lhs.compare(rhs) == 0;
  }
</pre>
<p>
&mdash; <i>end example</i>]
</p>
<pre>
template&lt;class charT, class traits&gt;
  constexpr bool operator==(basic_string_view&lt;charT, traits&gt; lhs,
                            basic_string_view&lt;charT, traits&gt; rhs) noexcept;
</pre>
<blockquote>
<p>
-2- <i>Returns:</i> <code><ins>lhs.size() == rhs.size() &amp;&amp;</ins> lhs.compare(rhs) == 0</code>.
</p>
</blockquote>
<pre>
template&lt;class charT, class traits&gt;
  constexpr bool operator!=(basic_string_view&lt;charT, traits&gt; lhs,
                            basic_string_view&lt;charT, traits&gt; rhs) noexcept;
</pre>
<blockquote>
<p>
-3- <i>Returns:</i> <code><ins>lhs.size() != rhs.size() ||</ins> lhs.compare(rhs) != 0</code>.
</p>
</blockquote>
</blockquote>
</li>
</ol>

</li>
</ol>





</body>
</html>
