首页 > 解决方案 > std::exchange 与 VC++ 和 gcc 的工作方式不同

问题描述

以下代码:

#include <utility>

int main()
{
  auto pos = 0;
  auto rel = pos - std::exchange(pos, pos + 1);

  return rel; // g++: 0, VC++: 1
}

如果您使用 VC++ 编译器在rextester上尝试代码,则结果为 1,使用godbolt上的 gcc 结果为 0(使用 rextester 的 gcc 显然不会返回结果)。

问题:为什么结果不同?

第二个问题:是否有任何工具可以检查该错误?任何铿锵警告?

我的猜测是,在评估另一个操作数之前std::exchange调用 VC++ ,而在 gcc 中并非如此。如果交换操作数,结果是 -1(或 255)VC++ 和 gcc。posstd::exchange

这可能是关于副作用的事情 - 以及std::exchange显然有副作用的调用。

幸运的是,在从 VC++ 转换到 gcc 后,我通过单元测试发现了这个错误 - 起初有点慌张,并将其归结为这个简单(非)工作示例。

标签: c++gccvisual-c++

解决方案


二元运算符是与序列点-相关联的注释,这意味着它没有指定表达式的顺序,并将在 中进行评估:ABA - B

考虑两个函数f()g()。在 C 和 C++ 中,+运算符不与序列点相关联,因此在表达式f()+g()中可能会先执行f()g()先执行。[...] 在 C 和 C++ 中,评估这样的表达式会产生未定义的行为。 [

因此,您的程序具有未定义的行为,对交叉编译器行为的任何分析都是徒劳的。


在标准语中,这是[intro.execution]/17 [强调我的]:

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是未排序的。[ <em>注意:在程序执行期间多次评估的表达式中,其子表达式的未排序和不确定排序的评估不需要在不同的评估中一致地执行。— <em>end note ] 运算符的操作数的值计算在运算符结果的值计算之前排序。如果内存位置上的副作用相对于同一内存位置上的另一个副作用或使用同一内存位置中任何对象的值的值计算是无序的,并且它们不是潜在的并发,行为是 undefined。[ <em>注意:下一节对潜在的并发计算施加了类似但更复杂的限制。— <em>结束注释]

[ <em>例子:

void g(int i) {
  i = 7, i++, i++;  // i becomes 9

  i = i++ + 1;      // the value of i is incremented
  i = i++ + i;      // the behavior is undefined
  i = i + 1;        // the value of i is incremented
}

 — <em>结束示例]


推荐阅读