带逗号的相互依赖初始化

Interdependent initialization with commas?

本文关键字:依赖 初始化      更新时间:2023-10-16

是否完全定义如下:

int x = 42, y = x;

。严格等价于:

int x = 42;
int y = x;

编辑:这个问题不是关于风格的(我知道这是错误的…),这个问题是"理论"

正确答案是

int x = 42, y = x;

int x = 42;
int y = x;

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


考虑标准§8声明器[dcl.decl]:

3声明中的每个初始化声明符被单独分析,就像它本身在一个声明中一样。

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

带有多个声明符的声明通常等同于相应的声明序列,每个声明符只有一个说明符。这是

T D1, D2,…Dn;

通常等价于

T D1;T D2;…T Dn;

其中T是一个decl-specifier-seq,每个Di是一个init-declarator。

  • 以上保证了x = 42y = x将分别求值。然而,正如@Praetorian在评论中正确指出的那样,脚注并不规范。

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

  • 有人可能认为逗号操作符是保证从左到右求值的。然而,事实并非如此。根据K &R [K &

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

这个问题出现在comp.lang.c++中。很久以前在主题init-declarator-list分析顺序下主持,结论是Yes

虽然我看到了完整表达式参数,但是我没有看到求值参数的顺序。所以我认为这是未指定的。

问题的相关部分是:

在下面的声明和定义中:

a = 2, b = a;

是否保证b总是初始化为2 ?如果是,那么我们能说a = 2总是在b = a之前被分析(或求值?)吗?

,答案的相关部分是:

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

及以下:

然而,在这个特殊的例子中,

分隔了声明器列表放到单独的声明器中;每个声明符都包含一个完整的表达式,声明符依次求值。

更新

是什么使每个初始化声明器成为完整表达式是微妙的,但据我所知,遵循了我在初始化器中使用的相同变量的多个突变列表中使用的相同逻辑c++ 11之前的未定义行为。在本例中,我们从section 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

在这两种情况下,我们都有=初始化子句,它将我们带到了赋值表达式,如果我们遵循5节中的语法,将我们带回到primary-expression,它可以给我们一个literalid-expression

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

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

根据1.914我们看到:

与a相关的每个值的计算和副作用全表达式在每次值计算和操作之前进行排序8.

至于评估的顺序,我认为这是没有指定的,适用于缺陷报告430的初始化列表的相同逻辑似乎也适用于这里。在c++ 11中,初始化列表的语言在8.5.4节中增加了以下内容:

在大括号初始化列表的初始化器列表中初始化子句,包括任何由包展开产生的子句(14.5.3),按它们出现的顺序求值。[…]

没有初始化式的等价物