<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2836: More string operations should be noexcept</title>
<meta property="og:title" content="Issue 2836: More string operations should be noexcept">
<meta property="og:description" content="C++ library issue. Status: Resolved">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2836.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#Resolved">Resolved</a> status.</em></p>
<h3 id="2836"><a href="lwg-defects.html#2836">2836</a>. More string operations should be <code>noexcept</code></h3>
<p><b>Section:</b> 27.4.3 <a href="https://wg21.link/basic.string">[basic.string]</a>, 27.4.3.8.2 <a href="https://wg21.link/string.find">[string.find]</a>, 27.4.3.8.4 <a href="https://wg21.link/string.compare">[string.compare]</a> <b>Status:</b> <a href="lwg-active.html#Resolved">Resolved</a>
 <b>Submitter:</b> Jonathan Wakely <b>Opened:</b> 2016-12-05 <b>Last modified:</b> 2023-02-07</p>
<p><b>Priority: </b>2
</p>
<p><b>View other</b> <a href="lwg-index-open.html#basic.string">active issues</a> in [basic.string].</p>
<p><b>View all other</b> <a href="lwg-index.html#basic.string">issues</a> in [basic.string].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Resolved">Resolved</a> status.</p>
<p><b>Discussion:</b></p>
<p>
Currently some overloads of <code>basic_string::find</code> are <code>noexcept</code> and some are not. Historically this 
was because some were specified in terms of constructing a temporary <code>basic_string</code>, which could throw. In 
practice creating a temporary (and potentially allocating memory) is a silly implementation, and so they could be 
<code>noexcept</code>. In the C++17 draft most of them have been changed to create a temporary <code>basic_string_view</code> 
instead, which can't throw anyway (P0254R2 made those changes).
<p/>
This is confusing for users, as they need to carefully check which overload their code will resolve to, and consider 
whether that overload can throw. Refactoring code can change whether it calls a throwing or non-throwing overload. 
This is an unnecessary burden on users when realistically none of the functions will ever throw.
<p/>
The <code>find</code>, <code>rfind</code>, <code>find_first_of</code>, <code>find_last_of</code>, <code>find_first_not_of</code> and 
<code>find_last_not_of</code> overloads that are defined in terms of <code>basic_string_view</code> should be <code>noexcept</code>, 
or "<i>Throws:</i> Nothing." for the ones with narrow contracts (even though those narrow contracts are not enforcable 
or testable).
<p/>
The remaining overloads that are still specified in terms of a temporary string could also be <code>noexcept</code>. They 
construct <code>basic_string</code> of length one (which won't throw for an SSO implementation anyway), but can easily be 
defined in terms of <code>basic_string_view</code> instead.
<p/>
There's one <code>basic_string::compare</code> overload that is still defined in terms of a temporary <code>basic_string</code>, 
which should be <code>basic_string_view</code> and so can also be <code>noexcept</code> (the other <code>compare</code> overloads can 
throw <code>out_of_range</code>).
</p>

<p><i>[2016-12-15, Tim Song comments]</i></p>

<p>
The following overloads invoking <code>basic_string_view&lt;charT&gt;(s, n)</code> are implicitly narrow-contract 
(the <code>basic_string_view</code> constructor requires <code>[s, s+n)</code> to be a valid range) and should be 
"<i>Throws:</i> Nothing" rather than <code>noexcept</code>:
</p>
<blockquote><pre>
size_type find(const charT* s, size_type pos, size_type n) const;
size_type rfind(const charT* s, size_type pos, size_type n) const;
size_type find_first_of(const charT* s, size_type pos, size_type n) const;
size_type find_last_of(const charT* s, size_type pos, size_type n) const;
size_type find_first_not_of(const charT* s, size_type pos, size_type n) const;
size_type find_last_not_of(const charT* s, size_type pos, size_type n) const;
</pre></blockquote>
<p>
Similarly, the <code>basic_string_view</code> constructor invoked by this overload of <code>compare</code> requires 
<code>[s, s + traits::length(s))</code> to be a valid range, and so it should be "<i>Throws:</i> Nothing" rather 
than <code>noexcept</code>:
</p>
<blockquote><pre>
int compare(const charT* s) const; 
</pre></blockquote>

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

<p>Priority 2; Jonathan to provide updated wording</p>

<p><i>[2017-10-22, Marshall comments]</i></p>

<p>libc++ already has all of these member fns marked as <code>noexcept</code></p>

<p><i>[2017-11 Albuquerque Saturday issues processing]</i></p>

<p>All the fns that take a <code>const char *</code> are narrow contract, and so can't be noexcept. Should be "Throws: Nothing" instead. Alisdair to re-word.</p>

<p><i>[2018-08 mailing list discussion]</i></p>

<p>This will be resolved by Tim's string rework paper.</p>

<p>Resolved by the adoption of <a href="https://wg21.link/P1148">P1148</a> in San Diego.</p>


<p id="res-2836"><b>Proposed resolution:</b></p>
<p>
This wording is relative to <a href="https://wg21.link/n4618">N4618</a>.
</p>
<ol>
<li><p>Change class template synopsis <code>std::basic_string</code>, 27.4.3 <a href="https://wg21.link/basic.string">[basic.string]</a>, as indicated:</p>
<blockquote>
<pre>
size_type find (basic_string_view&lt;charT, traits> sv,
                size_type pos = 0) const noexcept;
size_type find (const basic_string&amp; str, size_type pos = 0) const noexcept;
size_type find (const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
size_type find (const charT* s, size_type pos = 0) const;
size_type find (charT c, size_type pos = 0) const <ins>noexcept</ins>;
size_type rfind(basic_string_view&lt;charT, traits&gt; sv,
                size_type pos = npos) const noexcept;
size_type rfind(const basic_string&amp; str, size_type pos = npos) const noexcept;
size_type rfind(const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
size_type rfind(const charT* s, size_type pos = npos) const;
size_type rfind(charT c, size_type pos = npos) const <ins>noexcept</ins>;
size_type find_first_of(basic_string_view&lt;charT, traits&gt; sv,
                        size_type pos = 0) const noexcept;
size_type find_first_of(const basic_string&amp; str,
                        size_type pos = 0) const noexcept;
size_type find_first_of(const charT* s,
                        size_type pos, size_type n) const <ins>noexcept</ins>;
size_type find_first_of(const charT* s, size_type pos = 0) const;
size_type find_first_of(charT c, size_type pos = 0) const <ins>noexcept</ins>;
size_type find_last_of (basic_string_view&lt;charT, traits&gt; sv,
                        size_type pos = npos) const noexcept;
size_type find_last_of (const basic_string&amp; str,
                        size_type pos = npos) const noexcept;
size_type find_last_of (const charT* s,
                        size_type pos, size_type n) const <ins>noexcept</ins>;
size_type find_last_of (const charT* s, size_type pos = npos) const;
size_type find_last_of (charT c, size_type pos = npos) const <ins>noexcept</ins>;
size_type find_first_not_of(basic_string_view&lt;charT, traits&gt; sv,
                            size_type pos = 0) const noexcept;
size_type find_first_not_of(const basic_string&amp; str,
                            size_type pos = 0) const noexcept;
size_type find_first_not_of(const charT* s, size_type pos,
                            size_type n) const <ins>noexcept</ins>;
size_type find_first_not_of(const charT* s, size_type pos = 0) const;
size_type find_first_not_of(charT c, size_type pos = 0) const <ins>noexcept</ins>;
size_type find_last_not_of (basic_string_view&lt;charT, traits&gt; sv,
                            size_type pos = npos) const noexcept;
size_type find_last_not_of (const basic_string&amp; str,
                            size_type pos = npos) const noexcept;
size_type find_last_not_of (const charT* s, size_type pos,
                            size_type n) const <ins>noexcept</ins>;
size_type find_last_not_of (const charT* s,
                            size_type pos = npos) const;
size_type find_last_not_of (charT c, size_type pos = npos) const <ins>noexcept</ins>;

basic_string substr(size_type pos = 0, size_type n = npos) const;
int compare(basic_string_view&lt;charT, traits&gt; sv) const noexcept;
int compare(size_type pos1, size_type n1,
            basic_string_view&lt;charT, traits&gt; sv) const;
template&lt;class T&gt;
  int compare(size_type pos1, size_type n1, const T&amp; t,
              size_type pos2, size_type n2 = npos) const;
int compare(const basic_string&amp; str) const noexcept;
int compare(size_type pos1, size_type n1,
            const basic_string&amp; str) const;
int compare(size_type pos1, size_type n1,
            const basic_string&amp; str,
            size_type pos2, size_type n2 = npos) const;
int compare(const charT* s) const <ins>noexcept</ins>;
int compare(size_type pos1, size_type n1,
            const charT* s) const;
int compare(size_type pos1, size_type n1,
            const charT* s, size_type n2) const;
</pre>
</blockquote>
</li>

<li><p>Change 27.4.3.8.2 <a href="https://wg21.link/string.find">[string.find]</a> as indicated:</p>

<blockquote>
<pre>
size_type find(const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-5- <i>Returns:</i> <code>find(basic_string_view&lt;charT, traits&gt;(s, n), pos)</code>.
</p>
</blockquote>
<pre>
size_type find(const charT* s, size_type pos = 0) const;
</pre>
<blockquote>
<p>
-6- <i>Requires:</i> <code>s</code> points to an array of at least <code>traits::length(s) + 1</code> elements of <code>charT</code>.
<p/>
-7- <i>Returns:</i> <code>find(basic_string_view&lt;charT, traits&gt;(s), pos)</code>.
<p/>
<ins>-?- <i>Throws:</i> Nothing.</ins>
</p>
</blockquote>
<pre>
size_type find(charT c, size_type pos = 0) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-8- <i>Returns:</i> <code>find(basic_string<ins>_view&lt;charT, traits&gt;(addressof(c), 1)</ins><del>(1, c)</del>, pos)</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change  [string.rfind] as indicated:</p>

<blockquote>
<pre>
size_type rfind(const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-5- <i>Returns:</i> <code>rfind(basic_string_view&lt;charT, traits&gt;(s, n), pos)</code>.
</p>
</blockquote>
<pre>
size_type rfind(const charT* s, size_type pos = npos) const;
</pre>
<blockquote>
<p>
-6- <i>Requires:</i> <code>s</code> points to an array of at least <code>traits::length(s) + 1</code> elements of <code>charT</code>.
<p/>
-7- <i>Returns:</i> <code>rfind(basic_string_view&lt;charT, traits&gt;(s), pos)</code>.
<p/>
<ins>-?- <i>Throws:</i> Nothing.</ins>
</p>
</blockquote>
<pre>
size_type rfind(charT c, size_type pos = npos) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-8- <i>Returns:</i> <code>rfind(basic_string<ins>_view&lt;charT, traits&gt;(addressof(c), 1)</ins><del>(1, c)</del>, pos)</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change  [string.find.first.of] as indicated:</p>

<blockquote>
<pre>
size_type
  find_first_of(const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-5- <i>Returns:</i> <code>find_first_of(basic_string_view&lt;charT, traits&gt;(s, n), pos)</code>.
</p>
</blockquote>
<pre>
size_type find_first_of(const charT* s, size_type pos = 0) const;
</pre>
<blockquote>
<p>
-6- <i>Requires:</i> <code>s</code> points to an array of at least <code>traits::length(s) + 1</code> elements of <code>charT</code>.
<p/>
-7- <i>Returns:</i> <code>find_first_of(basic_string_view&lt;charT, traits&gt;(s), pos)</code>.
<p/>
<ins>-?- <i>Throws:</i> Nothing.</ins>
</p>
</blockquote>
<pre>
size_type find_first_of(charT c, size_type pos = 0) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-8- <i>Returns:</i> <code>find_first_of(basic_string<ins>_view&lt;charT, traits&gt;(addressof(c), 1)</ins><del>(1, c)</del>, pos)</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change  [string.find.last.of] as indicated:</p>

<blockquote>
<pre>
size_type find_last_of(const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-5- <i>Returns:</i> <code>find_last_of(basic_string_view&lt;charT, traits&gt;(s, n), pos)</code>.
</p>
</blockquote>
<pre>
size_type find_last_of(const charT* s, size_type pos = npos) const;
</pre>
<blockquote>
<p>
-6- <i>Requires:</i> <code>s</code> points to an array of at least <code>traits::length(s) + 1</code> elements of <code>charT</code>.
<p/>
-7- <i>Returns:</i> <code>find_last_of(basic_string_view&lt;charT, traits&gt;(s), pos)</code>.
<p/>
<ins>-?- <i>Throws:</i> Nothing.</ins>
</p>
</blockquote>
<pre>
size_type find_last_of(charT c, size_type pos = npos) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-8- <i>Returns:</i> <code>find_last_of(basic_string<ins>_view&lt;charT, traits&gt;(addressof(c), 1)</ins><del>(1, c)</del>, pos)</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change  [string.find.first.not.of] as indicated:</p>

<blockquote>
<pre>
size_type
  find_first_not_of(const charT* s, size_type pos, size_type n) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-5- <i>Returns:</i> <code>find_first_not_of(basic_string_view&lt;charT, traits&gt;(s, n), pos)</code>.
</p>
</blockquote>
<pre>
size_type find_first_not_of(const charT* s, size_type pos = 0) const;
</pre>
<blockquote>
<p>
-6- <i>Requires:</i> <code>s</code> points to an array of at least <code>traits::length(s) + 1</code> elements of <code>charT</code>.
<p/>
-7- <i>Returns:</i> <code>find_first_not_of(basic_string_view&lt;charT, traits&gt;(s), pos)</code>.
<p/>
<ins>-?- <i>Throws:</i> Nothing.</ins>
</p>
</blockquote>
<pre>
size_type find_first_not_of(charT c, size_type pos = 0) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-8- <i>Returns:</i> <code>find_first_not_of(basic_string<ins>_view&lt;charT, traits&gt;(addressof(c), 1)</ins><del>(1, c)</del>, pos)</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change  [string.find.last.not.of] as indicated:</p>

<blockquote>
<pre>
size_type find_last_not_of(const charT* s, size_type pos,
                           size_type n) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-5- <i>Returns:</i> <code>find_last_not_of(basic_string_view&lt;charT, traits&gt;(s, n), pos)</code>.
</p>
</blockquote>
<pre>
size_type find_last_not_of(const charT* s, size_type pos = npos) const;
</pre>
<blockquote>
<p>
-6- <i>Requires:</i> <code>s</code> points to an array of at least <code>traits::length(s) + 1</code> elements of <code>charT</code>.
<p/>
-7- <i>Returns:</i> <code>find_last_not_of(basic_string_view&lt;charT, traits&gt;(s), pos)</code>.
<p/>
<ins>-?- <i>Throws:</i> Nothing.</ins>
</p>
</blockquote>
<pre>
size_type find_last_not_of(charT c, size_type pos = npos) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-8- <i>Returns:</i> <code>find_last_not_of(basic_string<ins>_view&lt;charT, traits&gt;(addressof(c), 1)</ins><del>(1, c)</del>, pos)</code>.
</p>
</blockquote>
</blockquote>
</li>

<li><p>Change 27.4.3.8.4 <a href="https://wg21.link/string.compare">[string.compare]</a> as indicated:</p>

<blockquote>
<pre>
int compare(const charT* s) const <ins>noexcept</ins>;
</pre>
<blockquote>
<p>
-9- <i>Returns:</i> <code>compare(basic_string<ins>_view&lt;charT, traits&gt;</ins>(s))</code>.
</p>
</blockquote>
</blockquote>
</li>
</ol>





</body>
</html>
