gpt4 book ai didi

c++ - 为什么 move 返回一个右值引用参数需要用 std::move() 包装它?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:42:43 24 4
gpt4 key购买 nike

我正在阅读 Effective Modern C++ Item 25,第 172 页,它有一个例子来证明,如果你想移动返回一个右值引用参数,你需要用 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 作为一个命名变量,它本身就是一个左值,尽管它的类型是右值引用,所以它不能被移动。

最佳答案

表达式的类型不同于变量的类型,decltype 两者兼有。

decltype(w)

是变量w。

decltype((w))

是表达式 w 的类型(好吧 (w) 但它们是相同的)。

如果您有一个 foo&& 类型的变量,当在表达式中使用时,它的类型是 foo& —— 它被命名,因此是一个左值。

这有点道理。 foo&& 只是意味着它可以绑定(bind)到一个临时的。一旦绑定(bind),它就有了名字,可以多次使用。

任何可以多次使用的东西都不应隐式移动。

命名事物是左值的这条规则的唯一异常(exception)是返回规则的隐式移动。在少数情况下,省略会发生但由于某种原因被阻止,值会被隐式移动。这些异常(exception)不适用于此处。

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

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com