I'm trying to understand how pointers within objects behave when these objects are returned from functions, particularly in relation to copy elision and temporary object creation in C++ standards before and after C++17
. Below is my code example:
struct A {
void* p;
A() : p(this) {}
A(const A&); // Disable trivial copyability
};
A f() {
A x;
return x;
}
int main() {
A a; // OK: a.p points to a
A b = f(); // Concern: b.p could be dangling and point to the x inside f
A c = A(); // (until C++17) Concern: c.p could be dangling and point to a temporary
// (since C++17) OK: c.p points to c; no temporary is involved
}
Specifically, my concerns are:
In A b = f();
, is it correct that b.p could be a dangling pointer pointing to x inside f()
, depending on whether the compiler applies RVO before C++17?
For A c = A();
, I understand that in C++17, c.p
points directly to c with no temporary involved. But prior to C++17, could c.p
be a dangling pointer if a temporary object is created and then destroyed?
I would appreciate any clarification or insights into how these scenarios are handled in different C++ standards.
P.S. I specifically meant C++11
and C++14
as this code won't compile in C++03
When a copy is elided, it means that the the source and destination objects are the same object. There is no copy; that's the whole point.
In
A b = f();
, is it correct that b.p could be a dangling pointer pointing to x insidef()
, depending on whether the compiler applies RVO before C++17?
No. Because A
's copy constructor isn't implemented, the only way for it to possibly be returned from f
is via NRVO, in which case b
and x
are the same object. Behind the scenes main
passes a pointer to f
into which f
initializes its return object. When (N)RVO is applied, main
and f
simply both use that storage directly as b
and x
, respectively, instead of f
copying x
into the return value and then main
copying from the return value to b
.
For
A c = A();
, I understand that in C++17,c.p
points directly toc
with no temporary involved. But prior to C++17, couldc.p
be a dangling pointer if a temporary object is created and then destroyed?
No. Because A
's copy constructor isn't implemented there's no way for a temporary to be copied to c
. That means that the only way the program could possibly compile is if the copy is elided and c
is directly initialized.
Note that I don't think it's a good idea to rely on this behavior. It would be very easy for someone to get a random link error (because NRVO didn't get applied in some situation) and change the copy constructor to A(const A&) {}
to "fix" the problem, which could easily lead to dangling pointers.