c++ - std::initializer_list 会导致生命周期问题吗?

标签 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);

在我看来,初始化列表在传递给 vector 时指向已经销毁的临时对象。

结论

我认为所有这些示例都表明 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。带有悬空引用。

关于c++ - std::initializer_list 会导致生命周期问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21884499/

相关文章:

c++ - SDL 翻转两个表面

c++ - 积分转换的运行时检查

c++ - 你可以将 pimpl-Class 放入 vector 中吗

c++ - 初始化类成员 - 默认值还是成员初始化列表?

c++ - 我的 C 文件是由 Pro*C 生成的吗

c++ - 使用 cmake 添加 _CRT_SECURE_NO_WARNINGS 定义

C++ 如何从没有共享父类(super class)的类中调用公共(public)方法?

c++ - 在可变参数模板中使用声明

c++ - 为什么初始化列表不能作为main的参数?怎么提议呢?

c++ - 为什么 `initializer_list<pair>` 和 `initializer_list<tuple>` 的行为不同?