繁体   English   中英

std::exchange 与 VC++ 和 gcc 的工作方式不同

[英]std::exchange working differently with VC++ and 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 显然不会返回结果)。

问题:为什么结果不同?

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

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

这可能与副作用有关 - 并调用std::exchange显然有副作用。

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

二元运算符-是与序列点相关联的注释,这意味着它没有指定在A - B计算表达式AB顺序:

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

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


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

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

[示例:

 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 }

结束示例]

“未定义行为”是一个非常重要的理解原则。 如果您还没有了解它,那么您现在有机会解决这个问题。 如果行为没有明确定义,那么它就是undefined ,这意味着像这样的奇怪的事情可能并且将会发生。

对于给定的表达式a - f(a) ,绝对不需要编译器按从左到右的顺序执行。 定义的行为仅说明结果代表af(a)作为首先独立评估的。

如果您希望您的程序产生一致、正确的结果,那么挑战之一就是避免未定义的行为。 你怎么做到这一点? 编译器不会告诉你什么时候执行它,它会默默地发生,它甚至可能在你使用代码的所有条件下“工作”。

如果你想让它真正正确,你需要知道大量的规则,比如行为是未定义的,你只需要尊重它。 真的没有办法解决。 就像处理迭代器失效、使用后释放和使用未初始化的变量一样,您有相当大的责任来正确编码。 这就是为什么 C++ 很难正确编码的原因。

总之,其他语言对这样的表达式中的执行顺序做出了具体保证。 C++ 没有。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM