此文章首发于本人知乎https://zhuanlan.zhihu.com/p/81547559。
struct A {
A() : log{"default"} { cout << "A\n"; }
A(const A&) : log{"copy"} { cout << "A&\n"; }
A(A&&) : log{"move"} { cout << "A&&\n"; }
~A() { cout << "~A\n"; }
string log;
};
A create() {
A a;
return std::move(a);
}
A&& create_rref() {
A a;
return std::move(a);
}
int main() {
cout << "A create:\n";
auto a1 = create();
cout << a1.log << "\n";
cout << "A&& create_rref\n";
auto&& a2 = create_rref();
cout << a2.log << "\n";
cout << "End\n";
}
运行结果:
可以看到 a2.log 已经不是预期的值了。
此时有个坑,如果定义函数为返回右值引用,也就是 A&& create_rref(); 那么返回的结果就会引用到函数作用域内产生的对象中,没有调用移动构造函数,没有构造出新的对象,而是指向了那个即将被销毁的对象。所以函数结束的时候,可以看到析构函数的调用,作用域内对象销毁,这个右值引用也就为空了,并没有延续函数作用域内部的局部变量的声明周期,因此在这个引用上的操作也就十分危险。
如果是返回的是 A,也就是函数为 A create(); 那么在 return 的时候就会调用移动构造函数,创造出新的对象,随后函数作用域内部对象销毁,但是仍旧能够正常使用。
所以在函数作用域创建 unique_ptr 的时候,千万不要返回 std::move(up) 和右值引用。