【发布时间】:2014-02-19 15:09:12
【问题描述】:
在使用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);
现在我想起来了,它对我来说似乎是一个编译器错误,但它让我相信初始化列表实际上直接存储了指向文字、堆栈变量等的指针,而不是首先将它们复制到本地大批。
【问题讨论】:
-
所有三种情况都应该可以正常工作(但不要相信我的话,请阅读规范!)。
-
因为您将值复制到向量中,所以应该没问题。但据说使用
initializer_list作为容器是个坏主意。 -
@JoachimPileborg 好吧,我们已经看到它不起作用,尤其是示例 3。这是有道理的,因为列表仅存储开始和结束指针,并且在分配后将不再存在临时对象(除非有是初始化列表的一些特殊编译器处理)。
-
@Excelcius:你真的看到它不起作用了吗?在这种情况下,您的编译器有问题,并且没有为数组提供与
initializer_list相同的生命周期。 -
我也遇到了初始化器列表的编译器问题:stackoverflow.com/questions/20165166/…
标签: c++ c++11 initializer-list object-lifetime