将可变类型列表的扩展打包到复杂类型的初始化列表中——这合法吗?
Pack expansion of variadic list of types into initializer list of complex types - is it legal?
我想 "materialize" 可变类型列表到 initializer_list 相关值。
例如,有几个 std::integral_constant<T, x>
的 std::tuple
得到 std::initializer_list<T>{...}
。
在一般情况下,我想得到一些复杂类型的 initializer_list,比如 std::string
.
但是下面这个简单的例子让我在用 Clang 编译时崩溃了(尽管它适用于 GCC,至少在 Coliru 上是这样),所以我怀疑是 UB(或 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";
}
}
那么,这样的代码合法吗?在 C++11/14/17 中?
std::initializer_list
的底层数组其实是一个本地临时对象。从materialize
出来的时候已经被摧毁了。复制一个 std::initializer_list
不会复制底层数组,返回的 std::initializer_list
的内容总是无效的并且试图访问返回的 std::initializer_list
的内容会导致 UB.
(强调我的)
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.
关于 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.
和
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).
所以在这一行中
return {
std::to_string(Ts::value)...
};
您正在创建本地数组,initializer_list 保持指向此数组开头/结尾的指针,当函数超出范围时,您有悬空指针。
我想 "materialize" 可变类型列表到 initializer_list 相关值。
例如,有几个 std::integral_constant<T, x>
的 std::tuple
得到 std::initializer_list<T>{...}
。
在一般情况下,我想得到一些复杂类型的 initializer_list,比如 std::string
.
但是下面这个简单的例子让我在用 Clang 编译时崩溃了(尽管它适用于 GCC,至少在 Coliru 上是这样),所以我怀疑是 UB(或 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";
}
}
那么,这样的代码合法吗?在 C++11/14/17 中?
std::initializer_list
的底层数组其实是一个本地临时对象。从materialize
出来的时候已经被摧毁了。复制一个 std::initializer_list
不会复制底层数组,返回的 std::initializer_list
的内容总是无效的并且试图访问返回的 std::initializer_list
的内容会导致 UB.
(强调我的)
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.
关于 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.
和
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).
所以在这一行中
return {
std::to_string(Ts::value)...
};
您正在创建本地数组,initializer_list 保持指向此数组开头/结尾的指针,当函数超出范围时,您有悬空指针。