C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Proposal: Disallow string(_view) construction from two literals

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Wed, 28 Oct 2026 15:07:21 +0000
On Wed, 28 Oct 2026 at 13:55, Arthur O'Dwyer via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Wed, Oct 28, 2026 at 8:56 AM Johannes Kalmbach via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> We stumbled upon this when writing the very last example
>> ```
>> void f(std::vector<std::string>);
>> // Convenience overload for a single string
>> void f(std::string);
>>
>> for which f({"one", "two"}) is somewhat surprisingly ambiguous, as the
>> string overload is also eligible.
>> It could also be silently breaking when refactoring an API from
>> std::vector<std::string> to std::string (because it turned out that one
>> string was enough).
>> In that case having a set of constructor arguments that can be used for
>> both types is somewhat surprising, as it is, to my knowledge, not the case
>> for any other
>> constructor there.
>>
>
> The appropriate fix for this isn't to add a deleted constructor overload
> to `string`; it's to add the `explicit` keyword to `string`'s two-argument
> constructor.
>
> https://quuxplusone.github.io/blog/2023/04/08/most-ctors-should-be-explicit/
> Now, will there ever be the political will in WG21 to do this? No,
> definitely not. So I don't think there's anything to be done at the moment.
>
> Implicit conversions due to inappropriately non-explicit constructors are
> one of the major problems of the STL.
> Here's another example that I saw fairly often back in the day (like
> 2011-ish). Right when initializer-list constructors were a new thing and
> nobody really understood how to use them yet, it was a common (anti)pattern
> to wrap STL container initializer-lists in *two* sets of curly braces. In
> fact, the way I remember it, often std::array *wouldn't compile* unless
> you double-wrapped it (because it's a struct containing an array member,
> and brace elision wasn't fully implemented everywhere yet). This issue was
> fixed a long time ago now, of course, and single-curly-braces are 100%
> correct. But back then, I saw a lot of:
>
> // https://godbolt.org/z/vs9qzPre9
> int main() {
> std::vector<int> v1 = {{ 1, 2, 3 }}; // vector of size 3
> std::vector<const char*> v2 = {{ "a", "b", "c" }}; // vector of size 3
> std::vector<std::string> v3 = {{ "a", "b", "c" }}; // vector of size 3
> std::vector<int> v4 = {{ 1, 2 }}; // vector of size 2
> std::vector<const char*> v5 = {{ "a", "b" }}; // vector of size 2
> std::vector<std::string> v6 = {{ "a", "b" }}; *// UB goes boom*
>

Yes, the problem OP wants to solve for string and string_view is much
broader than just those two constructors.


>
> }
>
>
Tangentially, initializer-list constructors would have avoided some of
their problems if we had actually required the braces to be inside parens
or another set of braces, so that std::vector<int>({1,2}) was how you
called a constructor with a std::initializer_list, and
std::vector<int>{1,2} was unambiguously the vector(size_type, const T&)
constructor. Or we could have just never introduced "uniform" init at all,
and braces would only work for aggregates and for std::initializer_list
temporaries, then std::vector<int>{1,2} would just be ill-formed. Sigh.

Received on 2026-10-28 15:07:42