简体   繁体   English

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

[英]std::exchange working differently with VC++ and gcc

following code:以下代码:

#include <utility>

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

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

If you try the code on rextester with the VC++ compiler then the result is 1, with gcc on godbolt the result is 0 (results are apparently not returned with gcc using rextester).如果您使用 VC++ 编译器在rextester上尝试代码,则结果为 1,而godbolt 上的 gcc 结果为 0(使用 rextester 的 gcc 显然不会返回结果)。

Question : Why are the results different?问题:为什么结果不同?

And 2nd question : Are there any tools to check for that mistake?第二个问题:是否有任何工具可以检查该错误? Any clang warnings?任何叮当警告?

My guess is that std::exchange in VC++ is called before the other operand is evaluated, whereas in gcc this is not the case.我的猜测是 VC++ 中的std::exchange在评估另一个操作数之前被调用,而在 gcc 中并非如此。 If you swap the operands pos and std::exchange the result is -1 (or 255) both VC++ and with gcc.如果您交换操作数posstd::exchange ,结果是 -1(或 255)VC++ 和 gcc。

It is probably something about side effects - and calling std::exchange which clearly has a side-effect.这可能与副作用有关 - 并调用std::exchange显然有副作用。

Fortunately I caught the bug with a unit test after transitioning from VC++ to gcc - and was a bit flustered at first and boiled it down to this simple (not) working example.幸运的是,在从 VC++ 转换到 gcc 后,我通过单元测试发现了这个错误——起初有点慌张,把它归结为这个简单的(不是)工作示例。

Binary operator - is note associated with a sequence point , meaning it is not specified in which order expressions A and B will be evaluated in A - B :二元运算符-是与序列点相关联的注释,这意味着它没有指定在A - B计算表达式AB顺序:

Consider two functions f() and g() .考虑两个函数f()g() In C and C++, the + operator is not associated with a sequence point, and therefore in the expression f()+g() it is possible that either f() or g() will be executed first.在 C 和 C++ 中, +运算符不与序列点相关联,因此在表达式f()+g() ,可能先执行f()g() [...] In C and C++, evaluating such an expression yields undefined behavior.[ [...] 在 C 和 C++ 中,评估这样的表达式会产生未定义的行为。[

Thus, your program have undefined behaviour, and any analysis of cross-compiler behaviour is futile.因此,您的程序具有未定义的行为,任何对交叉编译器行为的分析都是徒劳的。


In standardese, this is [intro.execution]/17 [ emphasis mine]:在标准语中,这是[intro.execution]/17 [强调我的]:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced .除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是无序的 [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. [注意:在程序执行过程中多次求值的表达式中,其子表达式的无序和无序求值不需要在不同的求值中一致执行。 end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. end note ] 运算符的操作数的值计算在运算符的结果的值计算之前进行排序。 If a side effect on a memory location is unsequenced relative to either another side effect on the same memory location or a value computation using the value of any object in the same memory location , and they are not potentially concurrent, the behavior is undefined .如果一个内存位置的副作用相对于同一内存位置的另一个副作用或使用同一内存位置中任何对象的值的值计算是未排序的,并且它们不是潜在的并发,则行为是未定义的 [ Note: The next section imposes similar, but more complex restrictions on potentially concurrent computations. [注意:下一节对潜在的并发计算施加了类似但更复杂的限制。 end note ] 尾注]

[ Example: [示例:

 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 }

end example ] 结束示例]

"Undefined behaviour" is an extremely important principle to understand. “未定义行为”是一个非常重要的理解原则。 If you haven't learned about it yet, that's something you have an opportunity to address now.如果您还没有了解它,那么您现在有机会解决这个问题。 If behaviour is not specifically defined , then it's undefined , meaning weird things like this can and will happen.如果行为没有明确定义,那么它就是undefined ,这意味着像这样的奇怪的事情可能并且将会发生。

For a given expression a - f(a) there is absolutely no requirement that the compiler execute in left to right order.对于给定的表达式a - f(a) ,绝对不需要编译器按从左到右的顺序执行。 The defined behaviour only states that the outcome represents both a and f(a) as evaluated independently first.定义的行为仅说明结果代表af(a)作为首先独立评估的。

If you want your program to produce consistent, correct results then one of the challenges is to avoid undefined behaviour.如果您希望您的程序产生一致、正确的结果,那么挑战之一就是避免未定义的行为。 How do you do this?你怎么做到这一点? The compiler won't tell you when you do it, it will happen silently, and it might even "work" under all the conditions you use the code.编译器不会告诉你什么时候执行它,它会默默地发生,它甚至可能在你使用代码的所有条件下“工作”。

If you want it to be truly correct you need to know a multitude of rules like this one where the behaviour is undefined and you just need to respect that.如果你想让它真正正确,你需要知道大量的规则,比如行为是未定义的,你只需要尊重它。 There's really no way around it.真的没有办法解决。 Like when dealing with iterator invalidation, free after use, and use of uninitialized variables, you have considerable responsibility to code things correctly.就像处理迭代器失效、使用后释放和使用未初始化的变量一样,您有相当大的责任来正确编码。 This is why C++ can be exceptionally challenging to code correctly.这就是为什么 C++ 很难正确编码的原因。

In summary, other languages make specific guarantees about the order of execution in an expression like this.总之,其他语言对这样的表达式中的执行顺序做出了具体保证。 C++ does not. C++ 没有。

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

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