This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.
basic_string CTAD ambiguitySection: 27.4.3.3 [string.cons] Status: C++20 Submitter: Stephan T. Lavavej Opened: 2018-03-03 Last modified: 2021-02-25
Priority: Not Prioritized
View all other issues in [string.cons].
View all issues with C++20 status.
Discussion:
The following code fails to compile for surprising reasons.
#include <string>
#include <string_view>
using namespace std;
int main()
{
string s0;
basic_string s1(s0, 1, 1);
// WANT: basic_string(const basic_string&, size_type, size_type, const Allocator& = Allocator())
// CONFLICT: basic_string(size_type, charT, const Allocator&)
basic_string s2("cat"sv, 1, 1);
// WANT: basic_string(const T&, size_type, size_type, const Allocator& = Allocator())
// CONFLICT: basic_string(size_type, charT, const Allocator&)
basic_string s3("cat", 1);
// WANT: basic_string(const charT *, size_type, const Allocator& = Allocator())
// CONFLICT: basic_string(const charT *, const Allocator&)
}
For s1 and s2, the signature basic_string(size_type, charT, const Allocator&) participates in CTAD. size_type is non-deduced (it will be substituted later, so the compiler
can't immediately realize that s0 or "cat"sv are totally non-viable arguments).
charT is deduced to be int (weird, but not the problem). Finally, Allocator
is deduced to be int. Then the compiler tries to substitute for size_type, but
this ends up giving int to allocator_traits in a non-SFINAE context, so compilation fails.
s3 fails for a slightly different reason. basic_string(const charT *, const Allocator&) participates in CTAD, deducing charT to be char (good) and Allocator to be
int. This is an exact match, which is better than the constructor that the user actually wants
(where int would need to be converted to size_type, which is unsigned). So CTAD deduces basic_string<char, char_traits<char>, int>, which is the wrong type.
This problem appears to be unique to basic_string and its heavily overloaded set of constructors.
I haven't figured out how to fix it by adding (non-greedy) deduction guides. The conflicting constructors
are always considered during CTAD, regardless of whether deduction guides are provided that correspond
to the desired or conflicting constructors. (That's because deduction guides are preferred as a late
tiebreaker in overload resolution; if a constructor provides a better match it will be chosen before
tiebreaking.) It appears that we need to constrain the conflicting constructors themselves; this will
have no effect on actual usage (where Allocator will be an allocator) but will prevent CTAD
from considering them for non-allocators. As this is unusual, I believe it deserves a Note.
This has been implemented in MSVC.
[2018-3-14 Wednesday evening issues processing; move to Ready]
[2018-06 Rapperswil: Adopted]
Proposed resolution:
This wording is relative to N4727.
Edit 27.4.3.3 [string.cons] as indicated:
basic_string(const charT* s, const Allocator& a = Allocator());-14- Requires:
-15- Effects: Constructs an object of classspoints to an array of at leasttraits::length(s) + 1elements ofcharT.basic_stringand determines its initial string value from the array ofcharTof lengthtraits::length(s)whose first element is designated bys. -16- Postconditions:data()points at the first element of an allocated copy of the array whose first element is pointed at bys,size()is equal totraits::length(s), andcapacity()is a value at least as large assize(). -?- Remarks: Shall not participate in overload resolution ifAllocatoris a type that does not qualify as an allocator (23.2.2 [container.requirements.general]). [Note: This affects class template argument deduction. — end note]basic_string(size_type n, charT c, const Allocator& a = Allocator());-17- Requires:
-18- Effects: Constructs an object of classn < npos.basic_stringand determines its initial string value by repeating the char-like objectcfor allnelements. -19- Postconditions:data()points at the first element of an allocated array ofnelements, each storing the initial valuec,size()is equal ton, andcapacity()is a value at least as large assize(). -?- Remarks: Shall not participate in overload resolution ifAllocatoris a type that does not qualify as an allocator (23.2.2 [container.requirements.general]). [Note: This affects class template argument deduction. — end note]