basic_string::append/assign(NTBS, pos, n)
suboptimalSection: 27.4.3.7.2 [string.append], 27.4.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.
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).
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:
Modify 27.4.3.1 [basic.string.general], class template basic_string
synopsis, as indicated:
[…] // 27.4.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); […]
Modify 27.4.3.7.2 [string.append] as indicated:
template<class T> constexpr basic_string& append(const T& t);-3- Constraints:
(3.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
and(3.2) —
is_convertible_v<const T&, const charT*>
isfalse
.-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) —
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
and(?.2) —
is_convertible_v<const T&, const charT*>
isfalse
.-?- 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:
(5.1) —is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
and
(5.2) —.is_convertible_v<const T&, const charT*>
isfalse
-6- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return append(sv.substr(pos, n));
Modify 27.4.3.7.3 [string.assign] as indicated:
template<class T> constexpr basic_string& assign(const T& t);-4- Constraints:
(4.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
and(4.2) —
is_convertible_v<const T&, const charT*>
isfalse
.-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) —
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
and(?.2) —
is_convertible_v<const T&, const charT*>
isfalse
.-?- 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:
(6.1) —is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
and
(6.2) —.is_convertible_v<const T&, const charT*>
isfalse
-7- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return assign(sv.substr(pos, n));
Option B:
Modify 27.4.3.1 [basic.string.general], class template basic_string
synopsis, as indicated:
[…] // 27.4.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); […]
Modify 27.4.3.7.2 [string.append] as indicated:
constexpr basic_string& append(const charT* s, size_type n);-7- Preconditions:
-8- Effects: Appends a copy of the range[s, s + n)
is a valid 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));
Modify 27.4.3.7.3 [string.assign] as indicated:
constexpr basic_string& assign(const charT* s, size_type n);-8- Preconditions:
-9- Effects: Replaces the string controlled by[s, s + n)
is a valid range.*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));