std :: initalizer_list会导致生命周期问题吗?

时间:2014-02-19 15:09:12

标签: c++ c++11 initializer-list object-lifetime

使用std::initializer_list时遇到了一些困难。没过多久就意识到我更多地将它想象成一个容器,实际上它具有引用语义。所以我的问题是,以下哪个例子可能会导致问题,如果没有,为什么它们会起作用? 我应该补充一点,我正在使用VS2013,std::initializer_list仅使用开始和结束指针实现。

示例1

const auto tmpList = {1, 2, 3};
const vector<int> test(tmpList);

如果文字1,2和3存储在连续的内存块中,这可能有效。但这有保证吗?

示例2

const string a("foo");
int someOtherVariable = 10;
const string b("bar");
const auto tmpList = {a, b};
const vector<string> test(tmpList);

这应该不起作用,因为a和b可能位于堆栈的不同位置(请记住std::initializer_list只是保留指向第一个字符串的指针)。但话说回来,编译器应该能够以某种方式处理它,因为这应该在我的理解中起作用:

const vector<string> test({a, b});

或者是吗?

示例3

const auto tmpList = {string("foo"), string("bar")};
const vector<string> test(tmpList);

在我看来,初始化列表指向传递给向量时已经破坏的临时值。

结论

我认为所有这些示例都表明std::initializer_list不应该用作临时容器。如果是这种情况,是否应该禁止在任何地方存储初始化列表(除了作为函数的参数)?也许我也只是缺少一些编译器魔法,它会发现指针总是指向有效的,连续的内存。

解决方案&amp;背景

似乎所有上述例子都已明确定义。我的程序或VS2013 C ++编译器中似乎存在错误。当我使用这样的初始化列表时,首先出现问题:

const auto tmpList = {join(myList1), join(myList2)};
const vector<string> test(tmpList);

join是一个返回std::string的函数。在这种情况下,初始化列表包含2个条目,但第一个条目为空。将其拆分为可解决问题:

const auto str1 = join(myList1);
const auto str2 = join(myList2);
const auto tmpList = {str1, str2};
const vector<string> test(tmpList);

现在我想起来了,它看起来像是一个编译器错误,但它让我相信初始化列表实际上直接将指针存储到文字,堆栈变量等,而不是先将它们复制到本地阵列。

2 个答案:

答案 0 :(得分:4)

  

C ++ 11 8.5.4 / 6数组的生命周期与initializer_list对象的生命周期相同。

因此,您的任何示例都没有生命周期问题。你需要做一些令人费解的事情,比如

std::initializer_list<int> bad;
{
    bad = {1,2,3};
}
std::vector<int> test(bad);  // Boom!

遇到问题。

答案 1 :(得分:4)

您的所有示例都有明确定义的行为。来自§8.5.4/ 5

  

类型为std::initializer_list<E>的对象是从初始化列表构造的,就好像实现分配了Nconst EN元素的临时数组,其中std::initializer_list<E>是数字初始化列表中的元素。使用初始化列表的相应元素对该数组的每个元素进行复制初始化,并构造 struct X { X(std::initializer_list<double> v); }; X x{ 1,2,3 }; 对象以引用该数组。 ... [示例:

const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
  

初始化将以与此大致相同的方式实现:

struct A {
    std::initializer_list<int> i4;
    A() : i4{ 1, 2, 3 } {} // creates an A with a dangling reference
};
  

...- 结束示例]

此外,§8.5.4/ 6 是相关的

  

该数组与任何其他临时对象(12.2)具有相同的生命周期,除了从数组初始化initializer_list对象扩展了数组的生命周期,就像绑定对临时对象的引用一样。

标准甚至提供了有效和无效代码的示例。无效的代码示例如下:

i4

它是无效的,因为为构造函数体结束时为初始化程序列表创建的临时数组的生命周期结束,使{{1}}保留了悬空引用。