I wrote this code that looked very simple to me at a first glance. It modifies a variable that is referenced by a reference variable and then returns the value of the reference. A simplified version that reproduces the odd behavior looks like this:
#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;
}
However, when it is compiled with optimization enabled (g++ 7.5), it produces output different to non-optimized code (i.e. 9 without optimizations - as expected, and 3 with optimizations enabled).
I am aware of the volatile
keyword, which prevents the compiler from reordering and other optimizations in the presence of certain side-effects (e.g. async execution and hardware-specific stuff), and it helps in this case as well.
However, I do not understand why I need to declare reference b as volatile in this particular case? Where is the source of error in this code?
volatile
is very rarely the solution. Usually it's a bug. – Munson-O3
to get 9 output. @AlanBirtles – Apacea.b
is read twice. When optimizations are enabled (-O1
is sufficient) this read is optimized down to a single read, so the modified value is not picked in the return statement. I experimented with various-fno-strict-aliasing
,-fstrict-aliasing
flags (and the related warnings) but did not find anything that caused the compiler to re-reada.b
. (gcc 10.1, x64, via Compiler Explorer) – Folderol9
is a compiler bug – Hacker