std::pair内的初始化列表

26

这段代码:

#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上测试,以及在线编译器上测试。


1
为了方便起见:Godbolt链接 带有不带 std::pair - Max Langhof
2个回答

24

std::initializer_list 的作用不是用于存储数据,而是用于初始化。它内部只存储了第一个元素的指针和大小。在您的代码中,std::string 对象是临时对象,initializer_list 既不拥有它们,也不延长它们的生命周期,也不复制它们(因为它不是容器),因此它们在创建后立即超出范围,但您的 initializer_list 仍然持有对它们的指针。这就是为什么会出现段错误。

如果要存储数据,应该使用容器,如 std::vectorstd::array


这让我很困扰,这个可以编译。愚蠢的语言 :( - Lightness Races in Orbit
1
@LightnessRaceswithMonica 我对initializer_list有很多不满。它无法使用仅可移动对象,因此无法对vectorunique_ptr使用列表初始化,例如。initializer_list的大小不是编译时常量。而且std::vector<int>(3)std::vector<int>{3}执行完全不同的操作。这让我很难过:( - bolov
是的,一样... :( - Lightness Races in Orbit

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 的元素进行解引用的指针将导致未定义的行为。

演示实例在 此处


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接