I would like to "materialize" a variadic types list into an initializer_list of related values.
For example, having an std::tuple
of several std::integral_constant<T, x>
get an std::initializer_list<T>{...}
.
In general case, I would like to get initializer_list of some complex type, like std::string
.
But the following simple example gives me a crash when compiled by Clang (although it works with GCC, at least on Coliru), so I suspect UB (or bug in Clang):
template <class... Ts>
std::initializer_list<const std::string> materialize()
{
return {
std::to_string(Ts::value)...
};
}
void print_out()
{
for (const auto & x : materialize<std::true_type, std::false_type>()) {
std::cout << x << "\n";
}
}
Live on Coliru
So, is such code legal? In C++11/14/17?
Two things about initializer_list:
Initializer lists may be implemented as a pair of pointers or pointer and length. Copying a std::initializer_list does not copy the underlying objects.
and
The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended. The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation).
so in this line
return {
std::to_string(Ts::value)...
};
you are creating local array, initializer_list keeps pointer to the beginning / end of this array, when function goes out of scope you have dangling pointers.
The underlying array of std::initializer_list
is a local temporary object in fact. When get out of materialize
it has been destroyed. Copying an std::initializer_list
doesn't copy the underlying array, the content of the returned std::initializer_list
is always invalid and trying to access the content of the returned std::initializer_list
leads to UB.
(emphasis mine)
Initializer lists may be implemented as a pair of pointers or pointer and length. Copying a std::initializer_list does not copy the underlying objects.
The underlying array is a temporary array of type
const T[N]
, in which each element is copy-initialized (except that narrowing conversions are invalid) from the corresponding element of the original initializer list. The lifetime of the underlying array is the same as any other temporary object, except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary (with the same exceptions, such as for initializing a non-static class member). The underlying array may be allocated in read-only memory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With