花括号和圆括号中的参数求值顺序

4
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>

uint32_t func() { return rand() % 10; }

struct A {
  uint32_t _x, _y, _z;
  A(uint32_t x, uint32_t y, uint32_t z) : _x(x), _y(y), _z(z) {}
};

int main() {
  A a{func(), func(), func()};
  //A a(func(), func(), func());

  printf("%d %d %d\n", a._x, a._y, a._z);
  return 0;
}

GCC 9.1MSVC 19.22.27905 在使用花括号或括号时打印的顺序不同。Clang 8.0.0 对两种情况都会打印相同的顺序。

我在标准中找不到关于此事的说明,它是在标准中规定的还是由编译器决定输入参数的计算顺序?

2个回答

9

该顺序仅对大括号初始化列表[dcl.init.list]/4保证:

(我强调)

在大括号初始化列表的初始化器列表中,包括任何由打包扩展([temp.variadic])产生的初始化程序子句按照它们出现的顺序进行评估。也就是说,与给定初始化程序子句相关联的每个值计算和副作用都在逗号分隔的初始化器列表中后跟任何初始化程序子句之前的每个值计算和副作用之前进行序列化。[注意:此评估顺序不考虑初始化的语义;例如,当将初始化器列表的元素解释为构造函数调用的参数时,即使通常对于调用的参数没有排序约束,它也适用。 ——结束说明]

另一方面,函数调用中参数的评估顺序未指定的

未指定行为 - 程序的行为在不同的实现之间有所不同,符合规范的实现不需要记录每种行为的影响。例如,求值顺序、相同的字符串字面值是否不同、数组分配开销的数量等。每个未指定的行为都会产生一组有效结果。


1
请注意,在C++17之前,不同参数的子表达式可能会交错,例如f(std::unique_ptr<T>(new T),std::unique_ptr<T>(new T))如果其中一个对象构造失败,则可能会泄漏另一个对象。 - Davis Herring

1
似乎Clang在这个声明中评估参数。
A a( func(), func(), func() );

从左到右。而其他编译器则按照从右到左的顺序进行评估。
参数的评估顺序是未指定的。
至于花括号,则严格按照从左到右的顺序确定评估,并对每个表达式进行排序评估。

@NicolBolas 好的,可以说是有顺序的 :) - Vlad from Moscow
你的答案中的代码应该用括号而不是大括号吗? - L. F.
@L.F. 有人在没有通知我情况下更改了代码。 - Vlad from Moscow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接