我在第172页上阅读了有效的现代C++项目25,它有一个示例来说明,如果要移动返回右值引用参数,则需要用std::move(param)包装它。由于参数本身始终是一个左值,如果没有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)只是将其再次转换为右值引用。如果我取消注释TD行,则可以看到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是一个命名变量,但它本身是一个左值,尽管它的类型是右值引用,所以不能移动。
最佳答案
不,std::move(w)
强制转换为右值,而右值引用是左值。
函数passThroughMove
和passThrough
均按值返回。
但是,它们在内部创建此类返回值的方式不同。
在内部,passThroughMove
通过移动创建其返回值。通过移入一个新的Widget
对象(返回值),这就是std::move
对返回值的影响。另一方面,passThrough
通过复制创建自己的返回值。
事实,任务
Widget wt2 = passThrough(std::move(w2));
从右值完成操作不会改变
passThrough
被强制通过复制创建其返回值的事实。在代码的输出中,您会看到上述语义加上RVO的效果。如果没有RVO,则这两种分配都将导致两个附加的移动构造,并对其进行了优化。
关于c++ - 为什么移动返回右值引用参数需要用std::move()包装?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40187715/