其他分享
首页 > 其他分享> > c – std :: initalizer_list可以导致生命周期问题吗?

c – std :: initalizer_list可以导致生命周期问题吗?

作者:互联网

使用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不应该用作临时容器.如果是这种情况,是否应该禁止在任何地方存储初始化列表(除了作为函数的参数)?也许我也只是缺少一些编译器魔法,它会发现指针总是指向有效的,连续的内存.

解决方案&背景

似乎所有上述例子都有明确的定义.在我的程序或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);

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

解决方法:

您的所有示例都有明确定义的行为.从§8.5.4/ 5开始

An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array. … [ Example:

 struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };

The initialization will be implemented in a way roughly equivalent to this:

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

…—end example ]

此外,§8.5.4/ 6也是相关的

The array has the same lifetime as any other temporary object (12.2), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary.

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

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

它是无效的,因为为构造函数体结束时为初始化程序列表创建的临时数组的生命周期结束,使i4具有悬空引用.

标签:object-lifetime,c,c11,initializer-list
来源: https://codeday.me/bug/20190725/1531494.html