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

### 3662. `basic_string::append/assign(NTBS, pos, n)` suboptimal

Section: 21.3.3.7.2 [string.append], 21.3.3.7.3 [string.assign] Status: New Submitter: Jonathan Wakely Opened: 2022-01-21 Last modified: 2022-01-30

Priority: 3

View all other issues in [string.append].

View all issues with New status.

Discussion:

This will allocate temporary string, then copy one byte from it:

>     std::string s;
>     s.append("a very very long string that doesn't fit in SSO buffer", 13, 1);

The problem is that there is no overload accepting an NTBS there, so it converts to a temporary string using:

>     append(const basic_string&, size_t, size_t = npos);

C++17 added a new overload for a `string_view`:

>     append(const T&, size_t, size_t = npos);

But that overload is constrained to not allow `const char*` arguments, so we still create a temporary string.

If we're going to accept those arguments, we should do so efficiently, without forcing an unnecessary allocation.

We could do better if we split the `append(const T&, ...)` overload into:

>     append(const T&, size_t);
>     append(const T&, size_t, size_t);

and then remove the `!is_convertible_v<const T&, const charT*>` constraint from the second overload (Option A in the proposed resolution).

Alternatively, we can leave the `append(const T&, size_t, size_t)` overload alone and just add one taking exactly the arguments used above. That seems simpler to me, and I prefer that (Option B in the Proposed Resolution).

The same problem applies to `assign("...", pos, n)`.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4901.

> [Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A:

1. Modify 21.3.3.1 [basic.string.general], class template `basic_string` synopsis, as indicated:

   >     […]
   >     // 21.3.3.7 [string.modifiers], modifiers
   >     […]
   >     template<class T>
   >       constexpr basic_string& append(const T& t);
   >     template<class T>
   >       constexpr basic_string& append(const T& t, size_type pos);
   >     template<class T>
   >       constexpr basic_string& append(const T& t, size_type pos, size_type n = npos);
   >     constexpr basic_string& append(const charT* s, size_type n);
   >     […]
   >     template<class T>
   >       constexpr basic_string& assign(const T& t);
   >     template<class T>
   >       constexpr basic_string& assign(const T& t, size_type pos);
   >     template<class T>
   >       constexpr basic_string& assign(const T& t, size_type pos, size_type n = npos);
   >     constexpr basic_string& assign(const charT* s, size_type n);
   >     […]

2. Modify 21.3.3.7.2 [string.append] as indicated:

   >     template<class T>
   >       constexpr basic_string& append(const T& t);

   > > -3- Constraints:

   > > 1. (3.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (3.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -4- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return append(sv.data(), sv.size());

   >     template<class T>
   >       constexpr basic_string& append(const T& t, size_type pos);

   > > -?- Constraints:

   > > 1. (?.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (?.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -?- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return append(sv.substr(pos));

   >     template<class T>
   >       constexpr basic_string& append(const T& t, size_type pos, size_type n = npos);

   > > -5- Constraints:

   > > 1. (5.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (5.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -6- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return append(sv.substr(pos, n));

3. Modify 21.3.3.7.3 [string.assign] as indicated:

   >     template<class T>
   >       constexpr basic_string& assign(const T& t);

   > > -4- Constraints:

   > > 1. (4.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (4.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -5- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return assign(sv.data(), sv.size());

   >     template<class T>
   >       constexpr basic_string& assign(const T& t, size_type pos);

   > > -?- Constraints:

   > > 1. (?.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (?.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -?- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return assign(sv.substr(pos));

   >     template<class T>
   >       constexpr basic_string& assign(const T& t, size_type pos, size_type n = npos);

   > > -6- Constraints:

   > > 1. (6.1) — `is_convertible_v<const T&, basic_string_view<charT, traits>>` is `true` and

   > > 2. (6.2) — `is_convertible_v<const T&, const charT*>` is `false`.

   > > -7- Effects: Equivalent to:

   > > >     basic_string_view<charT, traits> sv = t;
   > > >     return assign(sv.substr(pos, n));

Option B:

1. Modify 21.3.3.1 [basic.string.general], class template `basic_string` synopsis, as indicated:

   >     […]
   >     // 21.3.3.7 [string.modifiers], modifiers
   >     […]
   >     constexpr basic_string& append(const charT* s, size_type n);
   >     constexpr basic_string& append(const charT* s);
   >     constexpr basic_string& append(const charT* s, size_type pos, size_type n);
   >     constexpr basic_string& append(size_type n, charT c);
   >     […]
   >     constexpr basic_string& assign(const charT* s, size_type n);
   >     constexpr basic_string& assign(const charT* s);
   >     constexpr basic_string& assign(const charT* s, size_type pos, size_type n);
   >     constexpr basic_string& assign(size_type n, charT c);
   >     […]

2. Modify 21.3.3.7.2 [string.append] as indicated:

   >     constexpr basic_string& append(const charT* s, size_type n);

   > > -7- Preconditions: `[s, s + n)` is a valid range.

   > > -8- Effects: Appends a copy of the range `[s, s + n)` to the string.

   > > -9- Returns: `*this`.

   >     constexpr basic_string& append(const charT* s);

   > > -10- Effects: Equivalent to: `return append(s, traits::length(s));`

   >     constexpr basic_string& append(const charT* s, size_type pos, size_type n);

   > > -?- Effects: Equivalent to: `return append(basic_string_view<charT, traits>(s).substr(pos, n));`

3. Modify 21.3.3.7.3 [string.assign] as indicated:

   >     constexpr basic_string& assign(const charT* s, size_type n);

   > > -8- Preconditions: `[s, s + n)` is a valid range.

   > > -9- Effects: Replaces the string controlled by `*this` with a copy of the range `[s, s + n)`.

   > > -10- Returns: `*this`.

   >     constexpr basic_string& assign(const charT* s);

   > > -11- Effects: Equivalent to: `return assign(s, traits::length(s));`

   >     constexpr basic_string& assign(const charT* s, size_type pos, size_type n);

   > > -?- Effects: Equivalent to: `return assign(basic_string_view<charT, traits>(s).substr(pos, n));`