While toying with RVO I encountered the following issue and I can't get my head around it.
#include <iostream>
struct A {
A* p = this;
};
A func()
{
return A();
}
int main()
{
A a = A();
std::cout << "address of a: " << &a << " - a.p = " << a.p << std::endl;
A b = func();
std::cout << "address of b: " << &b << " - b.p = " << b.p << std::endl;
}
Result:
address of a: 0x7ffe5b11b7a0 - a.p = 0x7ffe5b11b7a0
address of b: 0x7ffe5b11b790 - b.p = 0x7ffe5b11b770
All fine with a
. However note that b.p
doesn't actually point to b
as if it was pointing to a different object.
I'm using gcc 8.3.0 which supports C++17. My understanding is that from C++17 onwards, RVO is mandatory and initialisation from a prvalue (i.e. A b = func();
) should also trigger mandatory elision. Therefore, in theory, only one constructor should be called to initialise b
so p
should not be able to point to anything else than b
.
Could someone explain me what is happening here?
Note: initialising p
in a default constructor instead of using default member initialisation doesn't change the behaviour.
I toyed with this a bit more and realised that defining a default constructor AND any one of the following special functions makes p
point to b
as expected:
- destructor
- move constructor
- move assignment operator
- copy constructor
Instead of helping me understand the problem, this confused me even more...
As a side note, I have witnessed the exact same behaviour with gcc 11.0.0 and clang 11.0.0 (using Wandbox) so this is unlikely a bug.