C++ std::exchange与VC的工作方式不同++;和海湾合作委员会

C++ std::exchange与VC的工作方式不同++;和海湾合作委员会,c++,gcc,visual-c++,C++,Gcc,Visual C++,以下代码: #包括 int main() { 自动位置=0; auto rel=pos-std::exchange(pos,pos+1); return rel;//g++:0,VC++:1 } 如果您使用VC++编译器尝试代码,则结果为1,使用gcc,结果为0(使用rextester,gcc显然不会返回结果) 问题:为什么结果不同 和第二个问题:有什么工具可以检查这个错误吗?有什么叮当的警告吗 我猜VC++中的std::exchange在计算另一个操作数之前被调用,而在gcc中则不是这样。如

以下代码:

#包括
int main()
{
自动位置=0;
auto rel=pos-std::exchange(pos,pos+1);
return rel;//g++:0,VC++:1
}
如果您使用VC++编译器尝试代码,则结果为1,使用gcc,结果为0(使用rextester,gcc显然不会返回结果)

问题:为什么结果不同

第二个问题:有什么工具可以检查这个错误吗?有什么叮当的警告吗

我猜VC++中的
std::exchange
在计算另一个操作数之前被调用,而在gcc中则不是这样。如果交换操作数
pos
std::exchange
,结果是-1(或255),VC++和gcc都是

这可能与副作用有关——调用
std::exchange
显然有副作用


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

二进制运算符
-
与,这意味着未指定在
A-B
中计算表达式
A
B
的顺序:

考虑两个函数
f()
g()
。在C和C++中, +/<代码>运算符与序列点不相关,因此在表达式<代码> f~(+)+g-()/代码>中,可以先执行<代码> f>(<)>代码>或<代码>()>代码>。在C和C++中,评估这样的表达式产生未定义的行为。[</P> 因此,您的程序具有未定义的行为,对交叉编译器行为的任何分析都是徒劳的


在标准语中,这是[强调我的]:

除非另有说明,对单个 运算符和单个表达式的子表达式是 未排序的[ 注意:在计算次数超过 一次在程序执行期间,未排序且 其子表达式的不确定序列求值不需要 在不同的评估中保持一致。 — 尾注 ] 运算符操作数的值计算按顺序排列 在计算运算符结果的值之前。如果 对内存位置的副作用相对于或 对同一记忆位置或值的另一副作用 使用同一内存中任何对象的值进行计算 位置,并且它们没有潜在的并发性,行为是 未定义[ 注:下一节规定了类似的,但更多 对潜在并发计算的复杂限制。 — 结束 笔记 ]

[ 例如:

 — 结束示例 ]

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

对于给定的表达式
a-f(a)
,绝对不要求编译器按从左到右的顺序执行。定义的行为仅说明结果表示
a
f(a)
,这两个表达式都是先独立计算的

如果您希望程序产生一致、正确的结果,那么其中一个挑战就是避免未定义的行为。您如何做到这一点?编译器不会告诉您何时执行此操作,它将以静默方式进行,甚至可能在您使用代码的所有条件下“工作”

如果你想让它真正正确,你需要知道大量的规则,比如行为是未定义的,你只需要遵守这些规则。真的没有办法解决。比如在处理迭代器失效、使用后免费和使用未初始化变量时,你有相当大的责任编写正确的代码这就是为什么C++可以正确地挑战代码的原因。
总之,其他语言在这样的表达式中对执行顺序有了具体的保证。C++没有。

听起来像是未定义的行为。没有必要对它们进行左右的评价。@ TADMAN和您的注释究竟有帮助吗?我明白了要点:所以:基本上永远不要使用带有副作用的代码。带有二进制(或任何?)运算符的xpression。最好有编译警告。@michael_的编译器供应商通常关注(考虑到语言的复杂性)根据标准要求实现诊断。但是,一个好的静态分析工具可能能够警告您自己的情况。我看不到未定义的行为。函数体在表达式求值之前排序。子表达式求值的顺序未指定,但未定义。@j6t Whilst如果我没有弄错的话,子表达式的求值顺序是未指定的,适用于这里,因为其中一个子表达式对另一个子表达式读取的相同内存位置有副作用(“值计算”),并且OP:s示例未定义?是的,它适用。但是,修改仍然发生在
交换中,并且在函数调用的值计算之前排序。但是现在我不确定这是否提供了任何额外的保证。请注意,[intro.execution]的副作用/17讨论的是表达式中的运算符。调用函数内部发生的副作用不计算在内。OP的示例仅在赋值中有副作用。
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
}