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.

### 3663. `basic_string(const T&, const Alloc&)` turns moves into copies

Section: 21.3.3.3 [string.cons], 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.cons].

View all issues with New status.

Discussion:

This will do a copy not a move:

>     struct Str : std::string {
>       Str() = default;
>       Str(Str&& s) : std::string(std::move(s)) { }
>     };
>     Str s;
>     Str s2(std::move(s));

The problem is that the new C++17 constructor:

>     basic_string(const T&, const Alloc& = Alloc());

is an exact match, but the `basic_string` move constructor requires a derived-to-base conversion.

This is a regression since C++14, because the move constructor was called in C++14.

This problem also exists for `assign(const T&)` and `operator=(const T&)`.

We can fix this by constraining those functions with `!derived_from<T, basic_string>`, so that the move constructor is the only viable function. Libstdc++ has done something very similar since 2017, but using `!is_convertible<const T*, const basic_string*>` instead of `derived_from`.

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll. Jonathan to revise P/R to use `is_base_of` or `is_convertible` instead of `derived_from`.

Proposed resolution:

This wording is relative to N4901.

1. Modify 21.3.3.3 [string.cons] as indicated:

   >     template<class T>
   >       constexpr explicit basic_string(const T& t, const Allocator& a = Allocator());

   > > -7- Constraints:

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

   > > 2. (7.?) — `derived_from<T, basic_string>` is `false` and

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

   > > -8- Effects: Creates a variable, `sv`, as if by `basic_string_view<charT, traits> sv = t;` and then behaves the same as `basic_string(sv.data(), sv.size(), a)`.

   > […]

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

   > > -27- Constraints:

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

   > > 2. (27.?) — `derived_from<T, basic_string>` is `false` and

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

   > > -28- Effects: Equivalent to:

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

2. 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.?) — `derived_from<T, basic_string>` is `false` and

   > > 3. (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());