【问题标题】:Sequence Points and Method Chaining reloaded重新加载序列点和方法链
【发布时间】:2023-03-05 05:06:01
【问题描述】:

我读过这个:

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

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

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

这应该像这样返回 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

这对我在 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

这样的执行顺序是否明确定义?如果没有,我该如何保护自己,以后不会像这样在脚上开枪?

(用括号括起来似乎没有任何作用。)

【问题讨论】:

  • 操作数的计算可能是交错的。不要做有副作用的无序的事情来保护自己。 (括号对排序没有影响。)
  • Stream::seek() 返回 *this -- 函数的实际原型是什么?

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


【解决方案1】:

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

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

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

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

因为现在还没有很好地定义两个读取中的哪一个以哪个顺序发生 - 它可以首先调用第二个 read_integer(在偏移量 0 处)或在寻找并在偏移量 8 处读取。没人知道哪个,编译器甚至可能如果您对代码进行细微更改,请重新排列它们(例如,它决定以不同的顺序执行操作,因为您添加了另一个使用另一个寄存器的变量 -> 重新排列代码以更好地使用寄存器...)

解决办法是引入中间变量:

int a = stream.seek(4).read_integer();
int b = stream.seek(4).read_integer();

int should_be_zero = a - b; // Or b - a, if that's what you want... :)

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

【讨论】:

  • 谢谢,虽然这有点令人失望。我不禁想知道为什么牙套也没有帮助。
  • 因为大括号只影响优先顺序,而不影响实际的评估顺序。这反过来又是因为语言设计者不想制定过于严格的规则,因为这会影响优化的潜力,并可能影响在某些平台上实现语言的可能性。
【解决方案2】:

似乎允许这种行为:

1.9/15
除非另有说明,单个运算符的操作数的评估 和单个表达式的子表达式是无序的。 [ 注意:在计算期间不止一次计算的表达式中 程序的执行,未排序和不确定排序 不需要一致地执行其子表达式的评估 在不同的评价中。 —尾注] 的价值计算 运算符的操作数在计算值之前排序 运算符的结果。
[...]
调用函数时(无论该函数是否内联),与任何参数表达式或与指定被调用函数的后缀表达式相关的每个值计算和副作用都在执行主体中的每个表达式或语句之前进行排序称为函数。 [注意:与不同参数表达式相关的值计算和副作用是无序的。 —end note ]

Unsequenced 表示“左侧和右侧的计算可以交错”,例如“评估左侧表达式的一部分,评估右侧表达式,评估左侧表达式的其余部分”

【讨论】:

    猜你喜欢
    • 2011-07-28
    • 2011-06-06
    • 2011-10-12
    • 2010-09-20
    • 2013-03-13
    • 2015-09-15
    • 2013-09-09
    • 1970-01-01
    相关资源
    最近更新 更多