Reputation: 5552
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 in getProxy
Proxy(Proxy&& other)
move constructing p
~Proxy()
destructing the empty shell of the temporary that got its guts taken by move~Proxy() p
goes out of scope
class 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 calls close()
earlier than expectedProxy(Proxy&& other)
//moving after destruction gives p.pResource_
a value of nullptr
~Proxy()
//p
goes out of scopeThis 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.
Upvotes: 2
Views: 1620
Reputation: 110668
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.
Upvotes: 2
Reputation: 171127
getProxy()
returns a reference to a temporary, which goes out of scope at function end and results in a dangling reference.
Upvotes: 6