此代码:
#include <iostream>
#include <string>
std::pair<std::initializer_list<std::string>, int> groups{ { "A", "B" }, 0 };
int main()
{
for (const auto& i : groups.first)
{
std::cout << i << '\n';
}
return 0;
}
编译但返回段错误。为什么?
在gcc 8.3.0和在线编译器上进行了测试。
答案 0 :(得分:24)
std::initializer_list
并不意味着要存储,它只是为了...很好的初始化。在内部,它仅存储指向第一个元素和大小的指针。在您的代码中,std::string
对象是临时对象,initializer_list
既不拥有它们的所有权,也没有延长其寿命,也没有复制它们(因为它不是容器),因此它们在创建后立即超出范围,但是您的initializer_list
仍然拥有指向它们的指针。这就是为什么您会遇到细分错误。
要存储,您应该使用std::vector
或std::array
之类的容器。
答案 1 :(得分:3)
我将添加更多细节。 std::initializer_list
的基础数组的行为类似于临时对象。考虑以下课程:
struct X
{
X(int i) { std::cerr << "ctor\n"; }
~X() { std::cerr << "dtor\n"; }
};
及其在以下代码中的用法:
std::pair<const X&, int> p(1, 2);
std::cerr << "barrier\n";
它打印出来
ctor
dtor
barrier
从第一行开始,就创建了X
类型的临时实例(通过从1
转换构造函数)并将其销毁。然后将存储在p
中的引用悬空。
对于std::initializer_list
,如果您以这种方式使用它:
{
std::initializer_list<X> l { 1, 2 };
std::cerr << "barrier\n";
}
然后,只要l
退出,基础(临时)数组就存在。因此,输出为:
ctor
ctor
barrier
dtor
dtor
但是,如果您切换到
std::pair<std::initializer_list<X>, int> l { {1}, 2 };
std::cerr << "barrier\n";
再次输出
ctor
dtor
barrier
因为基础(临时)数组仅存在于第一行。取消引用l
元素的指针会导致行为未定义。
实时演示为here。