为什么move返回一个右值引用参数需要用std::move()包装它
Why move return an rvalue reference parameter need to wrap it with std::move()?
我正在阅读Effective Modern c++ Item 25,在172页,它有一个例子来证明,如果你想移动返回一个右值引用参数,你需要用std::move(param)来包装它。由于parameter本身总是一个左值,如果没有std::move(),它将被复制返回。
我不明白。如果std::move(param)只是将它接受的形参强制转换为右值引用,那么当param已经是右值引用时又有什么区别呢?
就像下面的代码:
#include <string>
#include <iostream>
#include <utility>
template<typename T>
class TD;
class Widget {
public:
explicit Widget(const std::string& name) : name(name) {
std::cout << "Widget created with name: " << name << ".n";
}
Widget(const Widget& w) : name(w.name) {
std::cout << "Widget " << name << " just got copied.n";
}
Widget(Widget&& w) : name(std::move(w.name)) {
std::cout << "Widget " << name << " just got moved.n";
}
private:
std::string name;
};
Widget passThroughMove(Widget&& w) {
// TD<decltype(w)> wType;
// TD<decltype(std::move(w))> mwType;
return std::move(w);
}
Widget passThrough(Widget&& w) {
return w;
}
int main() {
Widget w1("w1");
Widget w2("w2");
Widget wt1 = passThroughMove(std::move(w1));
Widget wt2 = passThrough(std::move(w2));
return 0;
}
输出:
Widget created with name: w1.
Widget created with name: w2.
Widget w1 just got moved.
Widget w2 just got copied.
passThroughMove (Widget&和;W), W的类型已经是右值引用,std::move(W)只是再次将其转换为右值引用。如果取消注释,可以看到decltype(w)和decltype(std::move(w))都是Widget &&:
move_parameter.cpp:27:21: error: implicit instantiation of undefined template 'TD<Widget &&>'
TD<decltype(w)> wType;
^
move_parameter.cpp:28:32: error: implicit instantiation of undefined template 'TD<Widget &&>'
TD<decltype(std::move(w))> mwType;
^
既然w和std::move(w)是相同的右值引用类型,为什么"return std::move(w)"移动w,而"return w"只是复制?
编辑:谢谢你的回答和评论。我现在有了更好的理解,但不确定它是否准确。因此std::move(w)返回一个右值引用,就像w本身一样。但是std::move(w)作为一个函数调用,它本身就是一个右值,所以它可以被移动。当w作为一个命名变量时,它本身是一个左值,尽管它的类型是右值引用,所以它不能被移动。表达式的类型与变量的类型不同,decltype
两者都是。
decltype(w)
是变量w。
decltype((w))
是表达式w
的类型(其实是(w)
,但它们是相同的)。
如果您有一个类型为foo&&
的变量,当在表达式中使用时,它的类型是foo&
——它被命名,因此是左值。
这是有道理的。foo&&
只是意味着它可以绑定到一个临时对象。一旦被绑定,它就有了一个名称,并且可以多次使用。
任何可以多次使用的东西都不应该被隐式地移出。
该规则的唯一例外是命名对象为左值,即隐式移动返回规则。在少数情况下,可能会发生省略,但由于某种原因被阻止,值被隐式移动。
passThroughMove (Widget&和;W), W的类型已经是右值引用,std::move(W)将其再次强制转换为右值引用。
所以std::move(w)返回一个右值引用,就像w本身一样。
不,std::move(w)
转换为右值,而右值引用是左值。
函数passThroughMove
和passThrough
都按值返回。然而,它们在内部创造这种回报价值的方式上有所不同。在内部,passThroughMove
通过move创建返回值。通过移动到一个新的Widget
对象(返回值)中创建它,这就是std::move
对返回值的影响。另一方面,passThrough
通过复制创建自己的返回值。
赋值
Widget wt2 = passThrough(std::move(w2));
是由右值完成的,这不会改变passThrough
被迫通过复制来创建返回值的事实。
- Usages of std::move
- 在C++中对T*类型执行std::move的意外行为
- 关于std::move的使用,是否有编译警告
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 通过实例理解std::move及其目的
- 返回一个带有 std::move 的对象并链接函数
- '[](std::list& list)<int>{return std::move(list)}(list)' 是否保证将 'list' 留空?
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- 为什么当我为 for(auto& it : myUnorderedMap) {... = std::move(it.second)} 时,我会得到一个 const 引用?
- std::move a const std::vector in a lambda capture
- "std::forward"和"std::move"真的不生成代码吗?
- std::vector move 而不是交换到空 vector 并释放存储
- 是否有必要使用 std::move?这不是已经是一个右值参考了吗?
- 如何在没有 std::move 的情况下移动临时对象
- 关于在成员重载中使用 std::move() 的问题
- 显式清除 std::move 之后的源资源
- std::move(key) 同时迭代unordered_map<字符串,字符串>?
- std::move(std::array) g++ vs visual-c++
- 使用 move std::string 的 .data() 成员不适用于小字符串?
- std::move(std::unique_ptr()) 组合是否有标准缩写