This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.

### 2836. More string operations should be `noexcept`

Section: 21.3.3 [basic.string], 21.3.3.8.2 [string.find], 99 [string.rfind], 99 [string.find.first.of], 99 [string.find.last.of], 99 [string.find.first.not.of], 99 [string.find.last.not.of], 21.3.3.8.4 [string.compare] Status: Resolved Submitter: Jonathan Wakely Opened: 2016-12-05 Last modified: 2020-09-06

Priority: 2

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with Resolved status.

Discussion:

Currently some overloads of `basic_string::find` are `noexcept` and some are not. Historically this was because some were specified in terms of constructing a temporary `basic_string`, which could throw. In practice creating a temporary (and potentially allocating memory) is a silly implementation, and so they could be `noexcept`. In the C++17 draft most of them have been changed to create a temporary `basic_string_view` instead, which can't throw anyway (P0254R2 made those changes).

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.

The `find`, `rfind`, `find_first_of`, `find_last_of`, `find_first_not_of` and `find_last_not_of` overloads that are defined in terms of `basic_string_view` should be `noexcept`, or "Throws: Nothing." for the ones with narrow contracts (even though those narrow contracts are not enforcable or testable).

The remaining overloads that are still specified in terms of a temporary string could also be `noexcept`. They construct `basic_string` of length one (which won't throw for an SSO implementation anyway), but can easily be defined in terms of `basic_string_view` instead.

There's one `basic_string::compare` overload that is still defined in terms of a temporary `basic_string`, which should be `basic_string_view` and so can also be `noexcept` (the other `compare` overloads can throw `out_of_range`).

[2016-12-15, Tim Song comments]

The following overloads invoking `basic_string_view<charT>(s, n)` are implicitly narrow-contract (the `basic_string_view` constructor requires `[s, s+n)` to be a valid range) and should be "Throws: Nothing" rather than `noexcept`:

>     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;

Similarly, the `basic_string_view` constructor invoked by this overload of `compare` requires `[s, s + traits::length(s))` to be a valid range, and so it should be "Throws: Nothing" rather than `noexcept`:

>     int compare(const charT* s) const;

[2017-01-27 Telecon]

Priority 2; Jonathan to provide updated wording

[2017-10-22, Marshall comments]

libc++ already has all of these member fns marked as `noexcept`

[2017-11 Albuquerque Saturday issues processing]

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

[2018-08 mailing list discussion]

This will be resolved by Tim's string rework paper.

Resolved by the adoption of P1148 in San Diego.

Proposed resolution:

This wording is relative to N4618.

1. Change class template synopsis `std::basic_string`, 21.3.3 [basic.string], as indicated:

   >     size_type find (basic_string_view<charT, traits> sv,
   >                     size_type pos = 0) const noexcept;
   >     size_type find (const basic_string& str, size_type pos = 0) const noexcept;
   >     size_type find (const charT* s, size_type pos, size_type n) const noexcept;
   >     size_type find (const charT* s, size_type pos = 0) const;
   >     size_type find (charT c, size_type pos = 0) const noexcept;
   >     size_type rfind(basic_string_view<charT, traits> sv,
   >                     size_type pos = npos) const noexcept;
   >     size_type rfind(const basic_string& str, size_type pos = npos) const noexcept;
   >     size_type rfind(const charT* s, size_type pos, size_type n) const noexcept;
   >     size_type rfind(const charT* s, size_type pos = npos) const;
   >     size_type rfind(charT c, size_type pos = npos) const noexcept;
   >     size_type find_first_of(basic_string_view<charT, traits> sv,
   >                             size_type pos = 0) const noexcept;
   >     size_type find_first_of(const basic_string& str,
   >                             size_type pos = 0) const noexcept;
   >     size_type find_first_of(const charT* s,
   >                             size_type pos, size_type n) const noexcept;
   >     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 noexcept;
   >     size_type find_last_of (basic_string_view<charT, traits> sv,
   >                             size_type pos = npos) const noexcept;
   >     size_type find_last_of (const basic_string& str,
   >                             size_type pos = npos) const noexcept;
   >     size_type find_last_of (const charT* s,
   >                             size_type pos, size_type n) const noexcept;
   >     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 noexcept;
   >     size_type find_first_not_of(basic_string_view<charT, traits> sv,
   >                                 size_type pos = 0) const noexcept;
   >     size_type find_first_not_of(const basic_string& str,
   >                                 size_type pos = 0) const noexcept;
   >     size_type find_first_not_of(const charT* s, size_type pos,
   >                                 size_type n) const noexcept;
   >     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 noexcept;
   >     size_type find_last_not_of (basic_string_view<charT, traits> sv,
   >                                 size_type pos = npos) const noexcept;
   >     size_type find_last_not_of (const basic_string& str,
   >                                 size_type pos = npos) const noexcept;
   >     size_type find_last_not_of (const charT* s, size_type pos,
   >                                 size_type n) const noexcept;
   >     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 noexcept;

   >     basic_string substr(size_type pos = 0, size_type n = npos) const;
   >     int compare(basic_string_view<charT, traits> sv) const noexcept;
   >     int compare(size_type pos1, size_type n1,
   >                 basic_string_view<charT, traits> sv) const;
   >     template<class T>
   >       int compare(size_type pos1, size_type n1, const T& t,
   >                   size_type pos2, size_type n2 = npos) const;
   >     int compare(const basic_string& str) const noexcept;
   >     int compare(size_type pos1, size_type n1,
   >                 const basic_string& str) const;
   >     int compare(size_type pos1, size_type n1,
   >                 const basic_string& str,
   >                 size_type pos2, size_type n2 = npos) const;
   >     int compare(const charT* s) const noexcept;
   >     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;

2. Change 21.3.3.8.2 [string.find] as indicated:

   >     size_type find(const charT* s, size_type pos, size_type n) const noexcept;

   > > -5- Returns: `find(basic_string_view<charT, traits>(s, n), pos)`.

   >     size_type find(const charT* s, size_type pos = 0) const;

   > > -6- Requires: `s` points to an array of at least `traits::length(s) + 1` elements of `charT`.

   > > -7- Returns: `find(basic_string_view<charT, traits>(s), pos)`.

   > > -?- Throws: Nothing.

   >     size_type find(charT c, size_type pos = 0) const noexcept;

   > > -8- Returns: `find(basic_string_view<charT, traits>(addressof(c), 1)(1, c), pos)`.

3. Change 99 [string.rfind] as indicated:

   >     size_type rfind(const charT* s, size_type pos, size_type n) const noexcept;

   > > -5- Returns: `rfind(basic_string_view<charT, traits>(s, n), pos)`.

   >     size_type rfind(const charT* s, size_type pos = npos) const;

   > > -6- Requires: `s` points to an array of at least `traits::length(s) + 1` elements of `charT`.

   > > -7- Returns: `rfind(basic_string_view<charT, traits>(s), pos)`.

   > > -?- Throws: Nothing.

   >     size_type rfind(charT c, size_type pos = npos) const noexcept;

   > > -8- Returns: `rfind(basic_string_view<charT, traits>(addressof(c), 1)(1, c), pos)`.

4. Change 99 [string.find.first.of] as indicated:

   >     size_type
   >       find_first_of(const charT* s, size_type pos, size_type n) const noexcept;

   > > -5- Returns: `find_first_of(basic_string_view<charT, traits>(s, n), pos)`.

   >     size_type find_first_of(const charT* s, size_type pos = 0) const;

   > > -6- Requires: `s` points to an array of at least `traits::length(s) + 1` elements of `charT`.

   > > -7- Returns: `find_first_of(basic_string_view<charT, traits>(s), pos)`.

   > > -?- Throws: Nothing.

   >     size_type find_first_of(charT c, size_type pos = 0) const noexcept;

   > > -8- Returns: `find_first_of(basic_string_view<charT, traits>(addressof(c), 1)(1, c), pos)`.

5. Change 99 [string.find.last.of] as indicated:

   >     size_type find_last_of(const charT* s, size_type pos, size_type n) const noexcept;

   > > -5- Returns: `find_last_of(basic_string_view<charT, traits>(s, n), pos)`.

   >     size_type find_last_of(const charT* s, size_type pos = npos) const;

   > > -6- Requires: `s` points to an array of at least `traits::length(s) + 1` elements of `charT`.

   > > -7- Returns: `find_last_of(basic_string_view<charT, traits>(s), pos)`.

   > > -?- Throws: Nothing.

   >     size_type find_last_of(charT c, size_type pos = npos) const noexcept;

   > > -8- Returns: `find_last_of(basic_string_view<charT, traits>(addressof(c), 1)(1, c), pos)`.

6. Change 99 [string.find.first.not.of] as indicated:

   >     size_type
   >       find_first_not_of(const charT* s, size_type pos, size_type n) const noexcept;

   > > -5- Returns: `find_first_not_of(basic_string_view<charT, traits>(s, n), pos)`.

   >     size_type find_first_not_of(const charT* s, size_type pos = 0) const;

   > > -6- Requires: `s` points to an array of at least `traits::length(s) + 1` elements of `charT`.

   > > -7- Returns: `find_first_not_of(basic_string_view<charT, traits>(s), pos)`.

   > > -?- Throws: Nothing.

   >     size_type find_first_not_of(charT c, size_type pos = 0) const noexcept;

   > > -8- Returns: `find_first_not_of(basic_string_view<charT, traits>(addressof(c), 1)(1, c), pos)`.

7. Change 99 [string.find.last.not.of] as indicated:

   >     size_type find_last_not_of(const charT* s, size_type pos,
   >                                size_type n) const noexcept;

   > > -5- Returns: `find_last_not_of(basic_string_view<charT, traits>(s, n), pos)`.

   >     size_type find_last_not_of(const charT* s, size_type pos = npos) const;

   > > -6- Requires: `s` points to an array of at least `traits::length(s) + 1` elements of `charT`.

   > > -7- Returns: `find_last_not_of(basic_string_view<charT, traits>(s), pos)`.

   > > -?- Throws: Nothing.

   >     size_type find_last_not_of(charT c, size_type pos = npos) const noexcept;

   > > -8- Returns: `find_last_not_of(basic_string_view<charT, traits>(addressof(c), 1)(1, c), pos)`.

8. Change 21.3.3.8.4 [string.compare] as indicated:

   >     int compare(const charT* s) const noexcept;

   > > -9- Returns: `compare(basic_string_view<charT, traits>(s))`.