Vad*_*nov 26 c++ gcc g++ volatile compiler-optimization
我写的这段代码乍一看很简单。它修改被引用变量引用的变量,然后返回引用的值。重现奇怪行为的简化版本如下所示:
#include <iostream>
using std::cout;
struct A {
int a;
int& b;
A(int x) : a(x), b(a) {}
A(const A& other) : a(other.a), b(a) {}
A() : a(0), b(a) {}
};
int foo(A a) {
a.a *= a.b;
return a.b;
}
int main() {
A a(3);
cout << foo(a) << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,当它在启用优化(g++ 7.5)的情况下编译时,它会产生与未优化代码不同的输出(即 9 个没有优化 -正如预期的那样,3 个启用了优化)。
我知道volatile关键字,它可以防止编译器在存在某些副作用(例如异步执行和特定于硬件的东西)的情况下重新排序和其他优化,并且在这种情况下也有帮助。
但是,我不明白为什么在这种特殊情况下需要将引用 b 声明为 volatile ?这段代码的错误来源在哪里?
Chr*_*phe 13
关于标准,我找不到 UB 的来源。在我看来,这就像优化器的一个错误,它不会注意到a.b并且a.a都指向同一个对象:
首先,foo()在副本上工作。我改成foo()引用传递,一直得到预期的结果。我怀疑引用的初始化有问题。但是提供的复制构造函数正确处理a.b.
然后我怀疑一些 UB 与同一表达式中不确定排序的操作的副作用有关。但是对 lhs 的副作用在*=rhs 之后排序,因此这里也没有 UB。
在*=语句之后添加一些日志记录使它意外地按预期工作。这看起来很奇怪:看起来像不遵守严格别名约束时遇到的常见问题,即当编译器没有意识到指向的对象被修改并优化代码时,就好像该值没有改变一样。在这种情况下,附加代码会导致重新加载正确的值并找到不同的结果并不罕见。
然而这里没有别名问题,因为原始成员和对它的引用都基于相同的类型。
当你排除了不可能的,剩下的,无论多么不可能,都一定是事实。
——亚瑟·柯南·道尔爵士
在消除了 OP 代码中的错误和 UB 之后,唯一剩下的可能性就是优化器中的错误。优化器似乎没有注意到 aa 和 ab 是同一个对象,它只是重用了已经在寄存器中的 ab 的最新已知值。