0

我的理解是不正确的。我认为移动的重点是强制 T&& 而不是 T 或 T& 过载。签名是

模板类型名 remove_reference::type&& move (T&& arg) noexcept;

我还认为 T&& 与 T& 相同,只是表示引用超出范围。据我了解,图像中的代码应该是安全的。

rv.append 要么创建一个新副本(超出范围),要么重用超出范围的 rv。我认为实现会复制,因为移动就在那里。然后它确实再次追加,然后是移动。当它返回时,我认为因为它是对临时的引用,所以它不知道 temp 来自哪里,应该将它复制到堆栈上并推迟它的生命周期

但是我不正确,修复程序与图像一起写在那里。为什么它不能那样工作?T&& 是否像我想的那样通过引用传递?关于右值引用,我还应该知道什么?

我也不明白std::unique_ptr<T> make_unique1(U&& u)这个例子中如何调用两个不同的函数

在此处输入图像描述

4

3 回答 3

4

rv.append 要么创建一个新副本(超出范围),要么重用超出范围的 rv。

不,它会rv自行修改,没有复制。它是一个成员函数调用,它修改rv.

我认为实现会复制,因为移动就在那里。

不,当rv.append()被调用时,它不受结果之后发生的其他调用的影响,它只是调用std::string::append(const char*)on rv

然后它确实再次追加,然后是移动。当它返回时,我认为因为它是对临时的引用,所以它不知道 temp 来自哪里,应该将它复制到堆栈上并推迟它的生命周期。

不,std::move只是强制转换rvstd::string&&,将任何东西复制到堆栈上没有任何魔法。返回值是绑定到 的右值引用rv,它是绑定到由 临时返回的右值引用meow()。然后在函数外部 aconst std::string&绑定到同一个临时变量,然后在分号处临时变量超出范围,r作为悬空引用留下。

于 2013-09-09T00:07:22.650 回答
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的返回值没有 from 的副本join,因为返回类型是一个引用,并且该引用的类型与表达式的类型相匹配move(rv)

的返回类型join是一个 xvalue,它是一个 glvalue 和一个 rvalue。因此,它直接绑定到 const lvalue ref r(不涉及副本)。

从返回的临时meow()值一直持续到 full-expression 结束const string& r = join(meow(), "purr");。由于它的生命周期没有延长,它被销毁并r成为一个悬空引用。

于 2013-09-09T00:07:24.260 回答
1

正如你所说,T&&是一个引用T&,所以返回一个对局部变量的右值引用与返回一个对局部变量的常规引用是一样的,在这两种情况下,局部变量(或在这种情况下是临时的)将在函数退出,你会得到一个无效的引用。生命周期延长没有帮助,因为临时绑定到函数返回值的生命周期只会延长到函数退出。

返回一个string值可以解决这个问题,因为返回值将从您使用创建的本地/临时变量中移动构造(作为新对象)move(rv.append(", ").append(ptr))。这样,当临时对象被销毁时,您返回的对象将不受影响。

另一种评论是,您的连接函数应该rv按值获取参数,而不是通过右值引用(string rv而不是string&& rv)。除非移动构造函数非常昂贵(它不应该如此),否则性能将是相同的,并且您还可以在临时值上调用您的函数。除了在移动构造函数中(或在模板函数中,但它实际上是一个通用引用)之外,我可以想到极少数情况下您希望将变量声明为右值引用。

于 2013-09-09T00:07:39.950 回答