8

我想将可变参数类型列表“具体化”为相关值的 initializer_list。例如,有std::tuple几个std::integral_constant<T, x>得到一个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 中?

4

2 回答 2

9

关于 initializer_list 的两件事:

初始化器列表可以实现为一对指针或指针和长度。复制 std::initializer_list不会复制 底层对象

在原始初始化列表对象的生命周期结束后,不能保证底层数组存在。std::initializer_list 的存储是未指定的(即它可以是自动的、临时的或静态的只读存储器,具体取决于情况)。

所以在这一行

return {
      std::to_string(Ts::value)...
    };

您正在创建本地数组,initializer_list 保持指向该数组的开头/结尾的指针,当函数超出范围时,您有悬空指针。

于 2018-09-11T06:37:41.053 回答
3

的底层数组std::initializer_list实际上是一个本地临时对象。出来的materialize时候已经毁了。复制一个std::initializer_list不会复制底层数组,返回的内容std::initializer_list总是无效的,试图访问返回的内容std::initializer_list会导致UB。

(强调我的)

初始化器列表可以实现为一对指针或指针和长度。复制 std::initializer_list 不会复制底层对象

底层数组是一个类型为 的临时数组const T[N],其中每个元素都是从原始初始化列表的对应元素复制初始化的(除了缩小转换无效)。底层数组的生命周期与任何其他临时对象相同,只是从数组初始化一个 initializer_list 对象会延长数组的生命周期,就像将引用绑定到临时对象一样(有相同的例外,例如初始化非-静态类成员)。底层数组可以分配在只读存储器中。

于 2018-09-11T06:37:25.260 回答