C++ Logo

std-proposals

Advanced search

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

From: Johannes Kalmbach <johannes.kalmbach_at_[hidden]>
Date: Wed, 28 Oct 2026 13:56:39 +0100
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.


Am Mi., 28. Okt. 2026 um 13:30 Uhr schrieb Andre Kostur <andre_at_[hidden]>:

> I'm curious as to what lead to that constructor call to be written? What
> was the person trying to accomplish by writing that? Attempting to
> initialize with a nullptr is sufficiently "special" to worry about. I'm
> not sure what would lead one to attempt to initialize a string from two
> string literals.
>
> On Wed, Oct 28, 2026 at 5:19 AM Johannes Kalmbach via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> Hi,
>> The following snippet compiles, but invokes UB, because overload
>> resolution treats the decayed pointers to the two string literals as begin
>> an end of the string:
>>
>> `std::string s{"undefined", "behavior"}`
>>
>> I would propose disallowing this antipattern, similar to P2166 disallows
>> string construction from `nullptr`.
>>
>> Fundamentally there are two approaches:
>> 1. Deleting that constructor explicitly
>> ```
>> template <size_t N, size_t M>
>> basic_string(const CharT(&a)[N], const CharT(&b)[M]) = delete;
>> ```
>> This works in the sense, that compilation never suceeds, but for two
>> literals with the same length the reason will be an ambiguous overload, and
>> not the deleted function. That could, if desired, be addressed by modifying
>> the constructor from two iterators:
>> ```
>> // as is
>> template <typename It>
>> basic_string(It, It); // leads to ambiguity
>>
>> // new, but semantics the same for existing correct programs
>> template <typename I1, typename I2> requires std::same_as<I1, I2>
>> basic_string(I1, I2);
>>
>>
>> 2. Constrain the constructor from two iterators to never accept two
>> literals. Unfortunately that is not easy, as the iterators are taken by
>> value, and so the constraint would always see the decayed type `const
>> CharT*`.
>> The only way to make that work would be to let that constructor take
>> references (assume a concept `array_like` that returns true for (reference
>> to) plain C-arrays:
>> ```
>> template <typename I1, typename I2> requires (std::same_as<I1, I2> &&
>> !array_like<I1> && !array_like<I2>)
>> basic_string(I1&&, I2&&);
>>
>> Version 2. has the disadvantage of being more intrusive wrt preexisting
>> constructors, and by using the antipattern of references to iterators.
>>
>> It however has the advante of making the following pattern work (which is
>> how I originally stumbled upon this):
>>
>> ```
>> void f(std::vector<std::string>);
>> // Convenience overload for a single string
>> void f(std::string);
>> ```
>> Where `f({"one", "two"})` is ambiguous today (and stays so with the
>> deleted constructor from 1.), but works as expected with a constrained
>> constructor from 2.
>>
>> Note:
>> There is a constructor to `basic_string_view` that suffers from exactly
>> the same issue, and can and would be addressed in exactly the same way.
>>
>> I am looking forward to you feedback.
>> Best regards
>> Johannes
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>

Received on 2026-10-28 12:56:56