当大括号初始值设定项列表中是否有序列点应用于构造函数时

Are there sequence points in braced initializer lists when they apply to constructors?

本文关键字:是否 构造函数 应用于 列表      更新时间:2023-10-16

根据 n4296 C++标准文档:

[dcl.init.list] (8.5.4.4) (pg223-224)

在大括号初始化列表的初始值设定项列表中, 初始值设定项子句,包括由包扩展产生的任何子句 (14.5.3),按其出现的顺序进行评估。那是 与给定相关的每个值计算和副作用 初始值设定项子句在每个值计算之前进行排序,并且 与它后面的任何初始值设定项子句相关的副作用 初始值设定项列表的逗号分隔列表。[注:本 无论 初始化;例如,当 初始值设定项列表被解释为构造函数调用的参数, 即使通常没有排序约束 调用的参数。—尾注 ]

(强调我的)

注释添加在此处:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1030

这对我来说是以下代码:

#include <iostream>
struct MyType {
  MyType(int i, int j, int k, int l)
    : sum(i + j + k + l)
  {
  }
  int sum;
};
int main()
{
  int i = 0;
  std::cout << MyType{ ++i, ++i, ++i, ++i }.sum << 'n';
}

应打印"10"。

这是我的推理:

  • MyType 正在通过大括号初始化列表进行初始化
  • 顺序计算大括号初始化列表
  • 即使它被解释为构造函数调用的参数
  • 这意味着它应该被评估为 MyType(1,2,3,4)

也就是说,上面的代码的行为应该与以下代码完全相同:

#include <initializer_list>
#include <iostream>
int main()
{
  int i = 0;
  std::initializer_list<int> il{++i, ++i, ++i, ++i};
  std::cout << *il.begin() + *(il.begin() + 1) + *(il.begin() + 2) + *(il.begin() + 3) << 'n';
}

但事实并非如此。第一个示例打印"16",第二个示例打印"10"

从字面上看,我能得到的每个供应商的每个编译器都打印了"16",似乎忽略了标准的那部分,也没有插入序号。

我在这里错过了什么?

注意:以下似乎与此问题有关:

  • (优化?关于 GCC 标准::线程的错误
  • https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253

答案似乎是肯定的,这是 GCC 和 MSVC 中的一个错误。

这是此问题的状态:

  1. 关于初始化列表规则,有几个针对 GCC 的错误。他们中的大多数完全没有被海湾合作委员会团队承认。这至少意味着 G++ 在这里确实存在错误,因为问题尚未因无效而关闭
  2. 我收到了MSVC编译器团队的非官方消息,这实际上是他们编译器中的一个错误,他们正在内部修复它。但是,我没有外部错误可以指出。从 MSVC 2015 更新 3 开始,旧行为仍然存在。
  3. Clang,在这一点上是迄今为止最迂腐的标准抱怨编译器,它以标准似乎阅读的方式实现它。

我个人的调查,在会议上与C++专家的讨论,以及我从编译器开发人员那里收到的非官方答案表明,这是MSVC和GCC中的一个错误,但我总是不愿意回答我自己关于StackOverflow的问题。但我们来了。

本说明与求值顺序无关。正如在其中一条评论中所述,这是关于将实际参数转换为右值的顺序,标准没有定义这种顺序。您应该会收到以下警告 (gcc):

17:58: warning: operation on 'i' may be undefined [-Wsequence-point]

我稍微修改了一个程序,以演示参数的评估如何使用 {} 和 ()。

通过这样的修改,程序不依赖于将左值转换为右值的顺序,因此不会有让您失望的歧义。

#include <iostream>
struct MyType {
  MyType(int i, int j)
    : sum(i + j)
  {
  }
  int sum;
};
int main()
{
  int i = 0;
  int a,b;
  std::cout << MyType{ (a = ++i), (b = ++i) }.sum << 'n';
  std::cout << "Here clauses are evaluated in order they appear: a=" << a << ", b=" << b << std::endl;
  i = 0;
  std::cout << MyType( (a = ++i), (b = ++i) ).sum << 'n';
  std::cout << "Here order of evaluation depends on implementation: a=" << a << ", b=" << b << std::endl;
}

以及这个程序对 clang 和 gcc 的输出:

铛:

3
Here clauses are evaluated in order they appear: a=1, b=2
3
Here order of evaluation depends on implementation: a=1, b=2

海湾合作委员会:

3
Here clauses are evaluated in order they appear: a=1, b=2
3
Here order of evaluation depends on implementation: a=2, b=1

如您所见,在大括号的情况下,子句在两个编译器下按出现顺序计算,这与你提供的注释相对应。

相关文章: