std::string{}.assign("ABCDE", 0, 1)
is ambiguousSection: 27.4.3.7.3 [string.assign] Status: C++17 Submitter: Marshall Clow Opened: 2016-07-30 Last modified: 2020-09-06
Priority: 1
View all other issues in [string.assign].
View all issues with C++17 status.
Discussion:
Before C++17, we had the following signature to std::basic_string
:
basic_string& assign(const basic_string& str, size_type pos, size_type n = npos);
Unlike most of the other member functions on std::basic_string
, there were not corresponding
versions that take a charT*
or (charT *, size)
.
basic_string& assign(basic_string_view<charT, traits> sv, size_type pos, size_type n = npos);
which made the code above ambiguous. There are two conversions from "const charT*
",
one to basic_string
, and the other to basic_string_view
, and they're both equally
good (in the view of the compiler).
insert(size_type pos1, const basic_string& str, size_type pos2, size_type n = npos); insert(size_type pos1, basic_string_view<charT, traits> sv, size_type pos2, size_type n = npos);
but I will file a separate issue (2757) for that.
A solution is to add even more overloads toassign
, to make it match all the other member
functions of basic_string
, which come in fours (string
, pointer
, pointer + size
,
string_view
).
[2016-08-03, Chicago, Robert Douglas provides wording]
[2016-08-05, Tim Song comments]
On the assumption that the basic_string
version is untouchable, I like the updated P/R, with a couple comments:
If it's constraining on is_convertible
to basic_string_view
, then I think it should take
by reference to avoid copying T
, which can be arbitrarily expensive. Both const T&
and
T&&
should work; the question is whether to accommodate non-const operator basic_string_view()
s
(which arguably shouldn't exist).
Minor issue: compare
tests is_convertible
and then uses direct-initialization syntax
(which is is_constructible
). They should match, because it's possible to have basic_string_view sv = t;
succeed yet basic_string_view sv(t);
fail.
[2016-08-05, Chicago LWG]
char const*
to basic_string_view
if possibleGiven feedback from discussion, we wish to go with the is_convertible_v
method, but change:
T const&
for each overloadis_convertible_v
to T const&
sv(t)
to sv = t
[2016-08, Chicago]
Fri PM: Move to Tentatively Ready
[2016-08-16, Jonathan Wakely reopens]
The P/R is not correct, the new overloads get chosen in preference to
the overloads taking const char*
when passed a char*
:
#include <string> int main () { std::string str("a"); char c = 'b'; str.replace(0, 1, &c, 1); if (str[0] != 'b') __builtin_abort(); }
With the resolution of 2758 this is now equivalent to:
str.replace(0, 1, string_view{&c, 1}, 1);
which replaces the character with string_view{"b", 1}.substr(1, npos)
i.e. an empty string.
value_type*
arguments.
I've implemented an alternative resolution, which would be specified as:
Remarks: This function shall not participate in overload resolution unless
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
andis_convertible_v<const T&, const charT*>
isfalse
.
All the overloads have the same problem.
This program prints no output in C++14, and we should make sure it prints no output in C++17 too:#include <string> int main() { std::string str("a"); char c[1] = { 'b' }; str.append(c, 1); if (str != "ab") puts("bad append"); str.assign(c, 1); if (str != "b") puts("bad assign"); str.insert(0, c, 1); if (str != "bb") puts("bad insert"); str.replace(0, 2, c, 1); if (str != "b") puts("bad replace"); if (str.compare(0, 1, c, 1)) puts("bad compare"); }
Ville and I considered "is_same_v<decay_t<T>, char*>
is false
" but
that would still select the wrong overload for an array of const char
,
because the new function template would be preferred to doing an array-to-pointer conversion to
call the old overload.
[2016-09-09 Issues Resolution Telecon]
Ville to provide updated wording; Marshall to implement
[2016-10-05]
Ville provides revised wording.
[2016-10 Telecon]
Ville's wording has been implemented in libstdc++ and libc++. Move this to Tentatively Ready and 2757 to Tentatively Resolved
Previous resolution [SUPERSEDED]:
This wording is relative to N4606.
In 27.4.3 [basic.string] modify the synopsis for
basic_string
as follows:namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>> class basic_string { public: […] template<class T> basic_string& append(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos); […] template<class T> basic_string& assign(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos); […] template<class T> basic_string& insert(size_type pos1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n = npos); […] template<class T> basic_string& replace(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos); […] template<class T> int compare(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos) const; […] }; }In 27.4.3.7.2 [string.append], modify
basic_string_view
overload as follows:template<class T> basic_string& append(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos);-7- Throws:
-8- Effects: Creates a variable,out_of_range
ifpos > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to append as the smaller ofn
andsv.size() - pos
and callsappend(sv.data() + pos, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
. -9- Returns:*this
.In 27.4.3.7.3 [string.assign], modify
basic_string_view
overload as follows:template<class T> basic_string& assign(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos);-9- Throws:
-10- Effects: Creates a variable,out_of_range
ifpos > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to assign as the smaller ofn
andsv.size() - pos
and callsassign(sv.data() + pos, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
. -11- Returns:*this
.In 27.4.3.7.4 [string.insert], modify
basic_string_view
overload as follows:template<class T> basic_string& insert(size_type pos1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n = npos);-6- Throws:
-7- Effects: Creates a variable,out_of_range
ifpos1 > size()
orpos2 > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to assign as the smaller ofn
andsv.size() - pos2
and callsinsert(pos1, sv.data() + pos2, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
. -8- Returns:*this
.In 27.4.3.7.6 [string.replace], modify
basic_string_view
overload as follows:template<class T> basic_string& replace(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos);-6- Throws:
-7- Effects: Creates a variable,out_of_range
ifpos1 > size()
orpos2 > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to be inserted as the smaller ofn2
andsv.size() - pos2
and callsreplace(pos1, n1, sv.data() + pos2, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
. -8- Returns:*this
.In 27.4.3.8.4 [string.compare], modify
basic_string_view
overload as follows:template<class T> int compare(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos) const;-4- Effects: Equivalent to:
{ basic_string_view<charT, traits> sv = t; return basic_string_view<charT, traits>(this.data(), pos1, n1).compare(sv, pos2, n2); }-?- Remarks: This function shall not participate in overload resolution unless
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
.
Proposed resolution:
This wording is relative to N4606.
In 27.4.3 [basic.string] modify the synopsis for basic_string
as follows:
namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>> class basic_string { public: […] template<class T> basic_string& append(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos); […] template<class T> basic_string& assign(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos); […] template<class T> basic_string& insert(size_type pos1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n = npos); […] template<class T> basic_string& replace(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos); […] template<class T> int compare(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos) const; […] }; }
In 27.4.3.7.2 [string.append], modify basic_string_view
overload as follows:
template<class T> basic_string& append(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos);-7- Throws:
-8- Effects: Creates a variable,out_of_range
ifpos > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to append as the smaller ofn
andsv.size() - pos
and callsappend(sv.data() + pos, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
andis_convertible_v<const T&, const charT*>
isfalse
. -9- Returns:*this
.
In 27.4.3.7.3 [string.assign], modify basic_string_view
overload as follows:
template<class T> basic_string& assign(basic_string_view<charT, traits> svconst T& t, size_type pos, size_type n = npos);-9- Throws:
-10- Effects: Creates a variable,out_of_range
ifpos > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to assign as the smaller ofn
andsv.size() - pos
and callsassign(sv.data() + pos, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
andis_convertible_v<const T&, const charT*>
isfalse
. -11- Returns:*this
.
In 27.4.3.7.4 [string.insert], modify basic_string_view
overload as follows:
template<class T> basic_string& insert(size_type pos1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n = npos);-6- Throws:
-7- Effects: Creates a variable,out_of_range
ifpos1 > size()
orpos2 > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to assign as the smaller ofn
andsv.size() - pos2
and callsinsert(pos1, sv.data() + pos2, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
andis_convertible_v<const T&, const charT*>
isfalse
. -8- Returns:*this
.
In 27.4.3.7.6 [string.replace], modify basic_string_view
overload as follows:
template<class T> basic_string& replace(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos);-6- Throws:
-7- Effects: Creates a variable,out_of_range
ifpos1 > size()
orpos2 > sv.size()
.sv
, as if bybasic_string_view<charT, traits> sv = t
. Determines the effective lengthrlen
of the string to be inserted as the smaller ofn2
andsv.size() - pos2
and callsreplace(pos1, n1, sv.data() + pos2, rlen)
. -?- Remarks: This function shall not participate in overload resolution unlessis_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
andis_convertible_v<const T&, const charT*>
isfalse
. -8- Returns:*this
.
In 27.4.3.8.4 [string.compare], modify basic_string_view
overload as follows:
[Drafting note: The wording changes below are for the same part of the standard as 2771. However, they do not conflict. This one changes the definition of the routine, while the other changes the "Effects". — end drafting note]
template<class T> int compare(size_type pos1, size_type n1,basic_string_view<charT, traits> svconst T& t, size_type pos2, size_type n2 = npos) const;-4- Effects: Equivalent to:
{ basic_string_view<charT, traits> sv = t; return basic_string_view<charT, traits>(this.data(), pos1, n1).compare(sv, pos2, n2); }-?- Remarks: This function shall not participate in overload resolution unless
is_convertible_v<const T&, basic_string_view<charT, traits>>
istrue
andis_convertible_v<const T&, const charT*>
isfalse
.