Recently a bug was discovered in our code where we were accidentally converting a std::array<char, N>
to std::string
. The buggy code doesn't compile in c 20 mode but compiles and //mostly// works in c 23 mode except for some edge cases with null termination. I'm using gcc 11.1.
#include <iostream>
#include <string>
#include <array>
int main()
{
std::array<char, 3> x = {'a', 'b', '\0'};
std::string s(x);
std::cout << "'" << s << "'" << " has length " << s.size() << std::endl;
//outputs "'ab' has length 3"
}
I suspect this may be related to P1989R2 - Range constructor for std::basic_string_view but I don't know what to make of it.
Is it a bug or just a weird behavior or what?
CodePudding user response:
Yes, it is related to P1989.
One of std::string
's constructors is:
template< class StringViewLike >
explicit constexpr basic_string( const StringViewLike& t,
const Allocator& alloc = Allocator() );
which is constrained on StringViewLike
being convertible to std::string_view
.
P1989 added this constructor to std::string_view
:
template <class R> requires /* ... */
constexpr string_view(R&&);
The constraints there require R
to be a contiguous_range
with appropriate value type and a few other things. Importantly, std::array<char, N>
meets those requirements, and this constructor is not explicit
.
As a result, you can now construct a std::string
from a std::array<char, N>
.
This led to P2499 (string_view
range constructor should be explicit
), which suggested what the title says, and P2516 (string_view
is implicitly convertible from what?), which suggested removing the constructor entirely. In the end, the decision was to make the constructor explicit
rather than removing it entirely (or keeping it implicit):
template< class R >
explicit constexpr basic_string_view( R&& r );
Because the string
constructor from StringViewLike
is constrained on convertible to (which requires implicit construction), this means std::string
is no longer constructible from std::array<char, N>
(or std::vector<char>
or ... ).