【问题标题】:Initializer list inside std::pairstd::pair 内的初始化列表
【发布时间】:2020-03-26 22:34:22
【问题描述】:

这段代码:

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

【问题讨论】:

  • 为方便起见:Godbolt 链接 withwithout std::pair

标签: c++ std std-pair


【解决方案1】:

std::initializer_list 不是用来存储的,它只是用于......以及初始化。在内部,它只存储指向第一个元素和大小的指针。在您的代码中,std::string 对象是临时的,initializer_list 既不拥有它们的所有权,也不延长它们的寿命,也不复制它们(因为它不是容器),因此它们在创建后立即超出范围,但你的 @987654324 @ 仍然持有指向它们的指针。这就是你得到分段错误的原因。

对于存储,您应该使用容器,例如 std::vectorstd::array

【讨论】:

  • 这是可编译的,这让我很困扰。愚蠢的语言:(
  • @LightnessRaceswithMonica 我对initializer_list 有很多意见。无法使用仅移动对象,因此您不能将 list init 与 unique_ptr 向量一起使用。 initializer_list 的大小不是编译时常量。事实上std::vector&lt;int&gt;(3)std::vector&lt;int&gt;{3} 做的事情完全不同。让我难过:(
  • 是的,一样... :(
【解决方案2】:

我只想添加更多细节。 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 2021-03-19
    • 1970-01-01
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多