我的理解不正确。我认为行动的重点是强迫T&&而不是T或T&超载。签名是
template typename remove_reference::type&& move (T&& arg) noexcept;
我还认为T&&和T&是一回事除了表示引用超出范围。根据我的理解,图像中的代码应该是安全的。
rv.append将创建一个新副本(将超出范围)或重用超出范围的rv。我认为实现是一个副本,因为移动就在那里。然后它再次追加,然后移动。当它返回时我想,因为它是对临时的引用,它不知道临时来自何处,应该将它复制到堆栈上postpone its lifetime。
但是我不正确,修复程序写在图像中。为什么不这样做?是T&&像我想的那样通过引用传递?关于Rvalue参考,我还应该知道什么?
我也不明白this example中的std::unique_ptr<T> make_unique1(U&& u)
如何调用两个不同的函数
答案 0 :(得分:4)
rv.append将创建一个新副本(超出范围)或重用超出范围的rv。
不,它会修改rv
本身,没有复制。这是一个修改rv
的成员函数调用。
我认为实现是一个副本,因为移动就在那里。
不,当调用rv.append()
时,它不会受到结果后发生的其他调用的影响,只会在std::string::append(const char*)
上调用rv
。
然后它会再次追加,然后再移动。当它返回时我想,因为它是对临时的引用,它不知道临时来自何处,应该将它复制到堆栈上并推迟它的生命周期。
不,std::move
只是将rv
转换为std::string&&
,没有任何魔法可以将任何内容复制到堆栈中。返回值是绑定到rv
的rvalue-reference,它是绑定到meow()
返回的临时值的rvalue-reference。然后在函数外部const std::string&
绑定到同一个临时,然后在分号处临时超出范围,留下r
作为悬空引用。
答案 1 :(得分:1)
右值引用有一个名称,因此它本身就是一个左值。 (这个想法是,如果你能够通过一个名称来引用一个对象,那么隐藏它就可能是不安全的,因为它的状态在移动后大多是未定义的。)
string&& join(string&& rv, const char* ptr) {
// rv is an lvalue here
rv.append(", "); // modifying rv
rv.append(ptr); // modifying again
return move(rv); // move required because rv is an lvalue
}
返回传递的第一个参数的右值引用。从rv
到join
的返回值没有副本,因为返回类型是引用,并且该引用的类型与表达式move(rv)
的类型匹配。
join
的返回类型是xvalue,它是一个glvalue和一个rvalue。因此它直接绑定到const lvalue ref r
(不涉及副本)。
从meow()
返回的临时值持续到完整表达式const string& r = join(meow(), "purr");
结束。由于它的生命周期没有延长,它被破坏,r
成为悬挂参考。
答案 2 :(得分:1)
如你所说,T&&
是一个类似于T&
的引用,因此返回对局部变量的rvalue引用与返回对局部变量的常规引用相同,在本例中都是本地变量当函数退出并且您将获得无效引用时,将销毁变量(或在这种情况下是临时的)。 Lifetime扩展没有帮助,因为只有函数返回值临时绑定的生命周期才会延长,直到函数退出。
返回string
值会修复此问题,因为返回值将从您使用move(rv.append(", ").append(ptr))
创建的本地/临时变量中移动构造(作为新对象)。这样,当临时被破坏时,您返回的对象将不受影响。
另外一条评论,您的加入函数应按值rv
取值,而不是通过右值参考(string rv
而不是string&& rv
)。除非移动构造函数非常昂贵(它不应该),性能将是相同的,您还可以在临时值上调用函数。我可以想到除了在移动构造函数中(或在模板函数中,它然后它实际上是一个通用引用)之外,你想要将变量声明为rvalue引用的极少数情况。