getProxy()
returns a reference to a temporary, which goes out of scope at function end and results in a dangling reference.
Why is the destructor called before the move constructor when returning a temporary (rvalue)
-
06-03-2022 - |
Frage
I suspect I don't understand something about move semantics. Given the following code I would expect the debugger (MSVC2010SP1) to call Proxy's members in the following order:
Proxy(Resource*)
constructing the temporary ingetProxy
Proxy(Proxy&& other)
move constructingp
~Proxy()
destructing the empty shell of the temporary that got its guts taken by move~Proxy() p
goes out of scopeclass Resource { void open(){} public: void close(){} Proxy && getProxy(); }; class Proxy { Resource *pResource_; Proxy(const Proxy& other); //disabled Proxy& operator=(const Proxy& other); //disabled public: Proxy(Resource *pResource):pResource_(pResource){} Proxy(Proxy&& other):pResource_(other.pResource_){other.pResource_ = nullptr;} ~Proxy() { if(pResource_) pResource_->close(); pResource_ = nullptr; } }; Proxy && Resource::getProxy() { open(); return Proxy(this); } //somewhere else, lets say in main() Resource r; { auto p = r.getProxy(); } // p goes out of scope
Instead the order is:
Proxy(Proxy*)
~Proxy()
//this already callsclose()
earlier than expectedProxy(Proxy&& other)
//moving after destruction givesp.pResource_
a value ofnullptr
~Proxy()
//p
goes out of scope
This makes no sense to me. What I'm trying to do is track the lifetime of the proxy class passing the job of closing the resource via the move constructor from one object to another.
Lösung
Andere Tipps
Returning by rvalue reference doesn't actually cause anything to be moved. It just returns by reference. However, it's different to returning an lvalue reference because an expression calling a function that returns an rvalue reference is an xvalue (as opposed to an lvalue). The xvalue (as a subset of rvalue expressions) can then be moved from. If you wanted to move from the returned object of a function returning lvalue reference, you would have to use std::move
to make it an rvalue.
You very rarely will want to actually return an rvalue reference. The only vaguely common use for it is to allow a private member of an object to be moved from. If you want an object to be moved when you return it from a function, just return it by value. In your case, if the return type of getProxy
was just Proxy
, the temporary would be moved from into the returned object and then that would be moved from into p
(save for any elision).
As you have it, your temporary object (contructed by Proxy(this)
) is destroyed at the end of the return
statement - this is the first call of the destructor. The returned reference is now referencing an invalid object and p
is constructed by moving from this invalid reference. That gives you undefined behaviour.