【问题标题】:Interdependent initialization with commas?用逗号进行相互依赖的初始化?
【发布时间】:2014-06-14 20:41:08
【问题描述】:

以下是否完美定义:

int x = 42, y = x;

即严格等同于:

int x = 42;
int y = x;

编辑:问题不在于风格(我知道这是错误的......),问题是“理论上的”

【问题讨论】:

标签: c++ c++11 initialization language-lawyer undefined-behavior


【解决方案1】:

正确答案是

int x = 42, y = x;

int x = 42;
int y = x;

通常是等价的(不严格)。


考虑到标准 § 8 声明符 [dcl.decl]

3 声明中的每个 init-declarator 都被单独分析,就好像它自己在声明中一样。

并在脚注[100]中进一步解释:

一个包含多个声明符的声明通常等同于相应的声明序列,每个声明只有一个 声明者。那是

T D1, D2, ... Dn;

通常等同于

T D1; T D2; ... T Dn;

其中 T 是一个声明说明符序列,每个 Di 是一个初始化声明符。

  • 以上保证x = 42y = x 将被单独评估。但是,正如@Praetorian 在 cmets 中正确指出的那样,脚注不是规范性的。

  • 这意味着评估顺序没有明确定义,实现者也可以以相反的顺序(即T Dn; ...T D2; T D1;)实现声明的评估。

  • 有人可能会争辩说,逗号运算符可以保证从左到右的评估。然而,事实并非如此。根据 K & R [K & R II, 3.6 p.63],这也适用于 C++:

分隔函数参数、声明中的变量等的逗号不是逗号运算符,不保证从左到右求值。

【讨论】:

  • 脚注是非规范的。我认为脚注中的 通常 为实现留下了足够的回旋余地,以便以相反的顺序评估它们,并且仍然是合规的。
  • @Praetorian 我同意脚注的非规范性,但标准中的引用清楚地表明它们是分开分析的。
  • 是的,这很清楚。但是将T D1, D2, ... Dn; 评估为T Dn; ... T D2; T D1; 也是在单独分析它们。
  • 逗号操作符是。但这不是逗号运算符,这只是 init-declarators 的分隔符 - §8/1 出现在声明中的 init-declarator-list 是逗号 -分隔的声明符序列
  • “通常等效,不严格” - 所以你是说y 有可能不会被初始化为42
【解决方案2】:

这个问题很久以前在comp.lang.c++.moderated 的主题init-declarator-list analysis order 下提出,结论是Yes

虽然我看到了 full-expression 参数,但我没有看到评估参数的顺序。所以我认为这是未指定的。

问题的相关部分是:

在此声明和定义中:

int a = 2, b = a;

是否保证 b 将始终初始化为 2 ?如果是,那么 我们可以说在 b = a 之前总是分析(或评估)a = 2 吗?

答案的相关部分是:

是的。严格来说,程序的可观察行为必须是 好像声明的“a = 2”部分的所有副作用 发生在“b = a”部分的评估开始之前。 (在 练习,当然,在这个简单的例子中,编译器可以分配 2 a 和 b 以任何顺序,甚至并行,因为这样做 会导致相同的可观察行为。)

再往下:

然而,在这种特殊情况下,它确实分隔了声明符 列出单独的声明器;每个声明符都包含一个完整的 表达式,并且声明符按顺序计算。

更新

使每个 init-declator 成为 完整表达式 的原因很微妙,但据我所知,遵循我在 Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11 中使用的相同逻辑。在这种情况下,我们从第 8 节中定义的语法开始:

init-declarator-list:
  init-declarator
  init-declarator-list , init-declarator
init-declarator:
  declarator initializeropt

下一个重点是在8.5 部分中介绍的初始化器 语法:

initializer:
  brace-or-equal-initializer
  ( expression-list )
brace-or-equal-initializer:
  = initializer-clause
  braced-init-list
initializer-clause:
  assignment-expression
  braced-init-list

在这两种情况下,我们都有 = initializer-clause 将我们带到 assignment-expression,如果我们遵循第 5 节中的语法,则会带来我们回到 primary-expression,它可以给我们一个 literalid-expression

所以我们确实有用语法逗号分隔的完整表达式,所以我们有:

int x = 42, y = x;
          ^      ^
          |      end full-expression
          end full-expression

根据1.914,我们看到:

与 全表达式在每个值计算和边之前排序 与要评估的下一个完整表达式相关联的效果。8.

至于评估的顺序,我认为这没有指定,适用于 defect report 430 的初始化列表的相同逻辑似乎也适用于这里。在 C++11 中,初始化器列表的语言在8.5.4 部分中添加了以下内容:

在花括号初始化列表的初始化列表中, 初始化子句,包括任何由包扩展产生的子句 (14.5.3),按照它们出现的顺序进行评估。 [...]

initializer 没有这样的等价物。

【讨论】:

  • 我更喜欢它,因为它包含了标准所说的细节。然而,我不确定你如何反驳我最初的批评:初始化器是一个表达式,而不是初始化。
  • 初始化器是一个声明器而不是一个表达式,因为它没有出现在5部分,但它可以包含和表达式,每个表达式都是一个完整的表达式,因为它不是一个子表达式。
  • 是的,这就是我的全部观点:) 标准在哪里保证按顺序完成初始化?
  • 另见CWG active #1342,我在 cmets 中将其链接到 OP。
  • 但是x的初始化不是完整表达式42的一部分,也不是表达式的副作用,是吗?
猜你喜欢
  • 2017-11-29
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多