我怀疑我对移动语义一无所知。鉴于以下代码,我希望调试器(MSVC2010SP1)按以下顺序调用Proxy的成员:
Proxy(Resource*)
在getProxy
Proxy(Proxy&& other)
移动构建p
~Proxy()
通过移动 ~Proxy() p
超出范围
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
相反,订单是:
Proxy(Proxy*)
~Proxy()
//这已经比预期更早地调用了close()
Proxy(Proxy&& other)
//在销毁后移动,p.pResource_
值为nullptr
~Proxy()
// p
超出范围这对我没有意义。我要做的是跟踪代理类的生命周期,将通过移动构造函数关闭资源的工作从一个对象传递到另一个对象。
答案 0 :(得分:5)
getProxy()
返回对临时的引用,该引用在函数结束时超出范围并导致悬空引用。
答案 1 :(得分:2)
通过右值引用返回实际上并不会导致任何内容被移动。它只是通过引用返回。但是,它与返回左值引用不同,因为调用返回右值引用的函数的表达式是xvalue(而不是左值)。然后可以移动xvalue(作为rvalue表达式的子集)。如果您想从返回左值引用的函数的返回对象移动,则必须使用std::move
使其成为右值。
您很少想要实际返回右值引用。它唯一模糊的用途是允许移动对象的私有成员。如果您希望在从函数返回对象时移动该对象,只需按值返回它即可。在您的情况下,如果getProxy
的返回类型只是Proxy
,则临时对象将被移动到返回的对象中,然后将其移至p
(除了任何省略) )。
如果你拥有它,你的临时对象(由Proxy(this)
构造)在return
语句的末尾被销毁 - 这是析构函数的第一次调用。返回的引用现在引用了一个无效对象,并且通过从此无效引用移动来构造p
。这会给你未定义的行为。