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

标签 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

这对我来说在 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(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... :)

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

关于c++ - 重新加载序列点和方法链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34381825/

相关文章:

c++ - 序列点和方法链

c++ - 自定义链表使用具有链表的结构创建 RtlValidateHeap 错误

c++ - 为什么没有默认的 move 分配/move 构造函数?

c++ - boost::共享_??对于非指针资源

c++ - 为什么 VS2015 中模板相关的嵌套类型名称不需要 typename 关键字?

linux - 如何在 C++ 中正确链接 .so 库?

JavaScript 可链接方法困境

python - 将 floodfill open cv 从 C++ 转换为 Python

c++ - 循环计算矩形的面积公式

CoffeeScript 链接调用