序列点和方法链接重新加载

时间:2015-12-20 14:07:24

标签: c++ c++11 method-chaining sequence-points

我读过这个:

...但我仍然不确定这应该如何表现:

int should_be_zero
    = stream.seek(4).read_integer()
    - stream.seek(4).read_integer();

Stream::seek()返回*thisseek / read_integer()分别在某些fseek上致电fread / FILE*

这应该像这样返回0:

  1. stream.seek(4)
  2. stream.read_integer()(在第4位,返回X,流位置提升为8)
  3. stream.seek(4)
  4. stream.read_integer()(在第4位,返回Y == X
  5. X - Y == 0
  6. 这对我来说非常适合gcc,MinGW和MinGW-w64。但是当我决定扩展对MSVC的编译器支持时,我发现它不再起作用并返回垃圾值。以下是MSVC上实际发生的事情:

    1. stream.seek(4)
    2. stream.seek(4)(再次)
    3. stream.read_integer()(在第4位,返回X,流位置提升为8)
    4. stream.read_integer()(在第8位,返回Y != X
    5. X - Y != 0
    6. 这样的执行顺序是否定义明确?如果没有,我怎样才能保护自己不会在未来像这样在脚下射击?

      (用括号包围来电似乎没有做任何事情。)

2 个答案:

答案 0 :(得分:2)

未定义表达式中的内部执行顺序。仅定义了运算符优先级的明显行为。

因此,在这种情况下,编译器必须两次调用stream.seek(4) [除非编译器发现它的结果相同"]和{{1两次。但是这些调用的顺序是不确定的(或者C ++标准中的任何术语) - 换句话说,编译器可以按照自己喜欢的方式对这四个调用进行排序。

如果您执行以下操作,您的代码将更具风险:

stream.read_integer()

因为它没有很好地定义两个读取中的哪一个现在按顺序发生 - 它可以先调用第二个read_integer(在偏移0处)或者在偏移8处的搜索和读取之后调用。没有人知道哪个,以及如果你对代码进行细微的修改,编译器甚至可以重新安排它们(例如,它决定以不同的顺序做事情,因为你添加了另一个使用另一个寄存器的变量 - >重新安排代码以更好地使用寄存器...... )

解决方案是引入中间变量:

 int x
    = stream.seek(4).read_integer()
    - stream.read_integer();  

这应该在每一段代码中完成,其中执行顺序对于代码的正确性很重要 - 并且要记住"副作用" (例如读取输入,写入输出,修改状态)肯定取决于执行顺序。

答案 1 :(得分:1)

似乎允许这种行为:

  

<强> 1.9 / 15
  除非另有说明,否则评估各个运营商的操作数   个别表达的子表达式未被排除。 [   注意:在表达式中,在表达式中多次计算   程序的执行,无序和不确定的顺序   不必一致地执行其子表达式的评估   在不同的评估中。 -end note]的计算值   操作符的操作数在值计算之前被排序   操作员的结果。
   [...]
  当调用函数时(无论函数是否为内联函数),与任何参数表达式相关联的每个值计算和副作用,或者使用指定被调用函数的后缀表达式,都会在执行每个表达式或语句之前对其进行排序。叫功能。 [注意:与不同参数表达式相关的值计算和副作用未被排序。 -end note]

无序表示&#34;左手侧和右手侧的评估可以交错排列&#34;喜欢&#34;评估左表达的一部分,评估正确的表达,评估左表达的其余部分&#34;