On 04.09.2019 14:20, Sam wrote:
> Ralf Goertz writes:
>
>> Hi,
>>
>> why does the declaration
>>
>> std::vector<std::string> pt({{"One","Two"}});
>>
>> throw:
>>
>> terminate called after throwing an instance of 'std::length_error'
>> what(): basic_string::_M_create
>
> Because this is undefined behavior.
That's the short explanation, but it would be even more informative to
mention why.
Like,
#include <iostream>
#include <string>
#include <vector>
using namespace std;
auto main()
-> int
{
const auto& data = "Hello";
std::vector<std::string> v( {{&data[0], &data[4]}} );
std::cout << v.front() << "!" << endl;
}
Result:
Hell!
I.e. it uses the `string` constructor that takes two iterators.
>> whereas neither
>>
>> std::vector<std::string> pt({{"One"}});
This looks like it would use the `string` constructor that takes a
pointer to C string.
>> nor
>>
>> std::vector<int> pt({{1,2}});
>
> If you actually examine the end result the last declaration, you will
> discover, to your surprise, a std::vector<int> with just one value, and
> that would hopefully be a big honking clue as to what this is doing.
No, this one is more tricky.
Apparently it uses the move constructor of `vector` to take a temporary
`vector` constructed from the inner braces as an `initializer_list`.
Disclaimer: the results are consistent with this explanation, and there
doesn't seem to be any other explanation, but I haven't debugged.
> { /* something */ } introduces a braced initialization list.
>
> { { /* something */ } } introduces a braced initialization list with one
> value.
>
>
> { { "One", "Two" } } attempts to construct a single std::string with two
> values. The only overloaded std::string constructor this matches is the
> one that takes a beginning iterator and an ending iterator of a sequence
> that forms the contents of the constructed std::string.
>
> Since "One" and "Two" decay to two character pointers which do not
> comprise a single sequence, you just nuked yourself from high orbit,
> hence the crash.
>
>> No warning is given during compilation by gcc version 9.2.1 20190820
>
> A compiler has no obligation to warn you when you're about to shoot
> yourself in a foot. It would be nice if it did, and in many cases it
> does; but you can't rely on your compiler to keep you from shooting
> yourself in a foot.
The problem is not the compiler or programmer. The problem IMO is some
political shenanigans in the C++ standardization committee, where they
chose to sabotage Bjarne's vision of uniform initialization by making
initializer lists take precedence in overload resolution. I prefer this
explanation of academics' childish evilness, to sheer incompetence at
that level, because the explanation of incompetence is too frightening.
The political view has so far, to my knowledge, yielded accurate
predictions and postdictions.
For example, the political view of childish sabotage indicates that the
filesystem::path UTF-8 functionality /will/ be sabotaged for Windows in
C++20, along with use of `u` prefix string literals. The `u` literals
don't make much sense in pure *nix-specific code, because literals are
UTF-8 encoded there anyway, and so the change in type doesn't matter
much there. But in Windows, those unfortunate souls who have tried to
embrace the UTF-8 world via `u` literals and filesystem::path, will have
some work to do, including implementing their own UTF-16 to UTF-8
conversion for getting an UTF-8 representation of a path.
- Alf