【问题标题】:Can it be safe to keep a copy of an std::initializer_list? What is the rationale?保留 std::initializer_list 的副本是否安全?理由是什么?
【发布时间】:2015-01-25 06:08:58
【问题描述】:

在我的环境中,std::initializer_list 被实现为指向第一个元素的指针和大小。仍然在我的特定设置中,我能够观察到:

  • 底层数据分配在当前函数帧中(因为指向第一个元素的指针是这样说的)
  • 从函数中按值返回 initializer_list 不会更改指针的值(导致数据未与 initializer_list 一起复制的结论)。

这使得复制 initializer_list 变得不安全,如果副本的寿命比原始对象长

  • C++ 标准的后续版本是否会保留此行为?
  • 同样重要的是,这种行为背后的基本原理是什么? (今天我真的很难受,所以我会天真地说这违背了“最小惊讶”的心爱原则)

【问题讨论】:

  • 没有。永远不要持有initializer_list。拒绝吧。如果你需要这样做,那你就错了。那不是它的用途。它是为了初始化而不是作为某个东西的持久句柄。
  • 我听说过的最好的说法是:initializer_list 不是一个容器,不要把它当作一个容器来对待。

标签: c++ c++11 c++14 initializer-list


【解决方案1】:

来自 C++11 标准,18.9 [support.initlist]:

2 类型为 initializer_list 的对象提供对类型为 const E 的对象数组的访问。 [ 注意:一对指针或一个指针加上一个长度将是 initializer_list 的明显表示。 initializer_list 用于实现 8.5.4 中指定的初始化列表。 复制初始化列表不会复制底层元素。 — 尾注]

这就像获取指向对象的指针。您还可以使指针比对象寿命更长。如果您想“安全地”执行此操作,请改为获取/存储元素向量。

复制元素会使它变得昂贵,因此没有人会使用它。 documentation available 非常清楚它的作用。

编辑:

这是 Stroustrup 对initializer_list 的提议:N2100。阅读它可能会对其设计决策有所启发。

【讨论】:

  • 自 c++14 以来,我们对其生命周期有更强有力的保证,请参阅您的链接:您的回答应该反映这一点
  • 我对 C++14 版本的阅读表明它仍然存在同样的问题:复制原始 initializer_list 不会延长它指向的数组的生命周期。
  • 因为传递指针+大小非常快。您应该在函数参数中使用它,而不是用于存储。要存储元素,您可以改用vector<T>,语法几乎没有变化。
  • 确实,他们本可以让它不可复制,所以你只能通过const initializer_list<T>&来使用它。 Stroustrup 认为,由于它是一个小对象,通过复制传递它可以简化 .begin().end()constexpr 的内联对 .size() 的评估:open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2100.pdf
  • @MattMcNabb 正如我在上面的评论中提到的,如果你想要一个副本,请使用矢量。从initializer_list 创建向量是很常见的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-15
  • 2013-02-18
  • 1970-01-01
  • 2015-02-14
  • 2020-09-01
  • 2012-05-31
相关资源
最近更新 更多