【问题标题】:Can I Reference Previous Members of an Initializer List?我可以引用初始值设定项列表的先前成员吗?
【发布时间】:2018-12-18 08:09:14
【问题描述】:

假设我想引用我已经定义的initializer_list 的成员。可以吗?

此代码在 Visual Studio 和 gcc 中编译并给出预期的:“13 55”,我只想知道它是合法的:

const int foo[2] = {13, foo[0] + 42};

【问题讨论】:

  • @NathanOliver 谢谢,我同意。但这是一个完全独立的问题。阅读有关结构的页面以找到有关数组的答案是没有建设性的。
  • DR1343 看起来还远远不够;需要的是一个绝对声明,即对于聚合初始化,初始化器不得在前一个元素的初始化完成之前进行评估。正如 Shafik 所说,目前似乎没有任何措辞来阻止对列表的所有元素进行评估,然后将结果应用于聚合
  • 这是一个带括号的初始化列表而不是initializer_list,不是吗?
  • @BaummitAugen Yes it is
  • @NathanOliver 哇...我很笨。感谢您的链接。

标签: c++ arrays language-lawyer initializer-list aggregate-initialization


【解决方案1】:

所以我们在这里看到的是 C++ 标准草案的 8.5.1 部分中涵盖的聚合初始化,它说:

聚合是一个数组或一个类 [...]

和:

当一个聚合被初始化列表初始化时,指定 在 8.5.4 中,初始化列表的元素被视为 聚合成员的初始化器,增加下标 或会员订单。每个成员都从 相应的初始化子句 [...]

虽然初始化聚合的每个成员的副作用应该在下一个之前排序似乎是合理的,因为初始化列表中的每个元素都是一个完整的表达式。该标准实际上并不能保证这一点,我们可以从defect report 1343 看到这一点:

目前的措辞并不表示非类对象的初始化是一个完整的表达式,但大概应该这样做。

还有注意事项:

聚合初始化也可能涉及多个完整表达式,因此上面对“非类对象的初始化”的限制是不正确的。

我们可以从相关的std-discussion topicRichard Smith 中看到:

[intro.execution]p10: "一个完整的表达式是一个不是 另一个表达式的子表达式。 [...] 如果语言结构 被定义为产生一个函数的隐式调用,使用 语言结构被认为是一种表达方式 这个定义。”

因为花括号初始化列表不是表达式,在这种情况下它 不会导致函数调用,5 和 s.i 是分开的 完整的表达式。那么:

[intro.execution]p14: "每个值的计算和副作用 与完整表达式相关联在每个值之前排序 与下一个完整表达式相关的计算和副作用 待评估。”

所以唯一的问题是,初始化 s.i 的副作用是什么? “与”完整表达式“5”的评估“相关”?我想 唯一合理的假设是:如果 5 正在初始化一个 类类型的成员,构造函数调用显然是 [intro.execution]p10 中定义的完整表达式,所以它 很自然地假设标量类型也是如此。

但是,我认为标准实际上并没有明确说明这一点 任何地方。

因此,目前标准并未对此进行规定,也不能依赖它,尽管如果实现没有按照您期望的方式处理它,我会感到惊讶。

对于像这样的简单情况,类似这样的东西似乎是更好的选择:

constexpr int value = 13 ;
const int foo[2] = {value, value+42};

C++17 的变化

proposal P0507R0: Core Issue 1343: Sequencing of non-class initialization 澄清了here 提出的完整表达式点,但没有回答有关初始化的副作用是否包含在完整表达式的评估中的问题。所以这并没有改变,这是未指定的。

这个问题的相关改动在[intro.execution]

组成表达式定义如下:

(9.1) — 表达式的组成表达式就是那个表达式。

(9.2) — 括号初始化列表或(可能带括号的)表达式列表的组成表达式是 各个列表元素的组成表达式。

(9.3) — 形式为 = initializer-clause 的大括号或等式初始化器的组成表达式是 初始化子句的组成表达式。 [ 例子:

struct A { int x; };
struct B { int y; struct A a; };
B b = { 5, { 1+1 } };

用于初始化b的初始化器的组成表达式是5和1+1。 ——结束示例]

[intro.execution]p12:

一个完整的表达式是

(12.1) — 未计算的操作数(第 8 条),

(12.2) — 一个常量表达式 (8.20),

(12.3) — 一个 init-declarator (Clause 11) 或一个 mem-initializer (15.6.2),包括 初始化器,

(12.4) — 在对象的生命周期结束时生成的析构函数调用,而不是临时对象 对象 (15.2),或

(12.5) — 一个表达式不是另一个表达式的子表达式,也不是其他表达式的一部分 完整的表达。

所以在这种情况下13foo[0] + 42 都是组成表达式,它们是完整表达式的一部分。这与analysis here 有所不同,后者假设它们每个都是自己的完整表达式。

C++20 的变化

Designated Initialization proposal: P0329 包含以下添加内容,似乎可以很好地定义:

在 11.6.1 [dcl.init.aggr] 中添加一个新段落:

聚合元素的初始化按元素顺序进行评估。那是, 与给定元素相关的所有值计算和副作用都按顺序排在其后的任何元素之前。

我们可以看到这反映在latest draft standard

【讨论】:

  • eel.is/c++draft/dcl.init.aggr#6 怎么样?它相当明确地表明它是允许的。
  • @Rakete1111 这是最近添加的 IIRC,当我有机会再次查找时会更新。
  • 哦,你是对的,它被添加了指定的初始化列表:) 谢谢。
  • @Rakete1111 我最近做了a twitter poll on a similar one,这个问题是actually similar to this one
  • 13foo[0] + 42 属于 one 完整表达式。那么为什么一个初始化器的副作用在另一个初始化器之前排序呢?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多