我在第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)强制转换为右值,而右值引用是左值。

函数passThroughMovepassThrough均按值返回。
但是,它们在内部创建此类返回值的方式不同。
在内部,passThroughMove通过移动创建其返回值。通过移入一个新的Widget对象(返回值),这就是std::move对返回值的影响。另一方面,passThrough通过复制创建自己的返回值。

事实,任务

Widget wt2 = passThrough(std::move(w2));

从右值完成操作不会改变passThrough被强制通过复制创建其返回值的事实。

在代码的输出中,您会看到上述语义加上RVO的效果。如果没有RVO,则这两种分配都将导致两个附加的移动构造,并对其进行了优化。

关于c++ - 为什么移动返回右值引用参数需要用std::move()包装?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40187715/

10-11 22:38
查看更多