Compiling the following with xlC on AIX results in code that prints "2 2". On Linux with gcc and clang it reliably produces "3 3".
#include <iostream>
struct Numbers
{
Numbers() : a(0) , b(0) { }
Numbers(int a, int b) : a(a), b(b) { }
int a;
int b;
};
Numbers combine(const Numbers& a, const Numbers& b)
{
Numbers x;
x.a = a.a + b.a;
x.b = a.b + b.b;
return x;
}
Numbers make()
{
Numbers a(1, 1);
Numbers b(2, 2);
a = combine(a, b);
return a;
}
int main()
{
Numbers a = make();
std::cerr << a.a << " " << a.b << "\n";
}
It looks to me like AIX is applying RVO to the return value of combine
, so when I create Numbers x
, it ends up overwriting my parameter a
with the default initialised x
.
Am I invoking some undefined behaviour here? I would expect that no modifications are made to a
until after combine(a, b)
has been evaluated and assigned to a
.
This is with: IBM XL C/C++ for AIX, V12.1 (5765-J02, 5725-C72) Version: 12.01.0000.0012
It looks like the compiler is performing copy elision on the copy assignment(!) where it could really only do so on an initialization. Which is to say that the compiler is indeed overwriting the object associated with your parameter a
when initializing x
. Having an application of RVO (for some definition of RVO) to the return value of combine
is itself not wrong. What is wrong is the target of the RVO (which should be a temporary in the scope of make
and not the object associated with a
in make
).
Adding a user provided copy assignment operator should work as a workaround:
Numbers &operator=(const Numbers &other) { a = other.a; b = other.b; return *this; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With