【发布时间】:2020-11-01 08:20:13
【问题描述】:
我写的这段代码乍一看很简单。它修改引用变量所引用的变量,然后返回引用的值。重现奇怪行为的简化版本如下所示:
#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;
}
但是,当它在启用优化 (g++ 7.5) 的情况下编译时,它产生的输出与未优化的代码不同(即 9 没有优化 - 符合预期,3 启用了优化)。
我知道volatile 关键字,它可以防止编译器在存在某些副作用(例如异步执行和特定于硬件的东西)的情况下进行重新排序和其他优化,并且在这种情况下也有帮助。
但是,我不明白为什么我需要在这种特殊情况下将引用 b 声明为 volatile?这段代码的错误来源在哪里?
【问题讨论】:
-
volatile是很少的解决方案。通常这是一个错误。 -
@AlanBirtles 帖子中的代码重现了所描述的问题,并且已经很短了。但是,我添加了缺失的包含以使其可编译,并在帖子中增加了对预期输出的强调。
-
编译器看到的是
a.b被读取了两次。启用优化后(-O1就足够了)此读取被优化为单次读取,因此在返回语句中不会选择修改后的值。我尝试了各种-fno-strict-aliasing、-fstrict-aliasing标志(以及相关的警告),但没有发现任何导致编译器重新读取a.b的内容。 (gcc 10.1,x64,通过编译器资源管理器) -
这种行为看起来像教科书的未定义行为。然而,我越看它,就越觉得它是一个 gcc 错误。每个对象都被初始化。因为没有悬空的参考。如果我们使所有内容都正确,则 constexpr clang 会给出错误“对 'a' 的子对象的引用不是常量表达式”。然而 gcc 给出了一个错误的错误信息:“'A(3)' is not a constant expression because it refer to an incompletely initialized variable”这可能只是一个措辞不当的错误文本,或者可能是 gcc 认为以某种方式子对象在这里未初始化。
-
这样的案例,对极其简单的代码的优化被搞砸了,总是让我想知道有多少关键代码被错误编译并且没有人注意到
标签: c++ gcc g++ compiler-optimization volatile