当括号初始化列表应用于构造函数时,是否存在序列点?

时间:2016-04-25 22:41:33

标签: c++ c++11 gcc visual-c++ clang

根据n4296 C ++标准文件:

  

[dcl.init.list](8.5.4.4)(第223-224页)

     

在braced-init-list的初始化列表中,   initializer-clause,包括包扩展产生的任何条款   (14.5.3),按照它们出现的顺序进行评估。那是,   与给定相关的每个值计算和副作用   initializer-clause在每个值计算之前排序   与其后面的任何初始化子句相关联的副作用   以逗号分隔的初始化列表列表。 [注意:这个   无论语义如何,评估排序都成立   初始化;例如,它适用于。的元素   initializer-list被解释为构造函数调用的参数,   即使通常没有排序限制   电话的参数。 - 尾注]

     

(强调我的)

这里添加了注释: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';
}

应打印&#34; 10&#34;。

这是我的理由:

  • 正在通过braced-init-list
  • 初始化MyType
  • 按顺序评估braced-init-lists
  • 即使被解释为构造函数调用的参数
  • 这意味着它应该被评估为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';
}

但事实并非如此。第一个例子打印&#39; 16&#39;第二个例子打印&#39; 10&#39;

从每个供应商那里得到的所有编译器都可以打印出来&#39; 16&#39; 看似忽略标准的那一部分而不插入序列点。

我在这里缺少什么?

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

2 个答案:

答案 0 :(得分:3)

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

这是此问题的状态:

  1. 针对初始列表规则,GCC存在一些错误。他们中的大多数人都完全没有得到海湾合作委员会团队的承认。这至少意味着G ++确实存在错误,因为问题没有被关闭为无效
  2. 我收到了来自MSVC编译器团队的非官方消息,这实际上是他们编译器中的一个错误,他们正在内部修复它。但是,我没有任何外部错误指向。从MSVC 2015 Update 3开始,旧行为仍然存在。
  3. Clang,此时是迄今为止最迂腐的标准投诉编译器,以标准似乎阅读的方式实现它。
  4. 我的个人调查,与C ++专家在会议上的讨论以及我从编译器开发人员那里收到的非官方答案表明这是MSVC和GCC中的一个错误,但我总是不愿回答我自己关于StackOverflow的问题。但我们在这里。

答案 1 :(得分:0)

本说明与评估顺序无关。正如其中一条评论中所述,它是关于将实际参数转换为rvalues的顺序,而标准并没有定义这样的顺序。 您应该收到以下警告(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

GCC

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

如您所见,在大括号的情况下,子句按照两个编译器下的外观顺序进行评估,这与您提供的注释相对应。