【问题标题】:Why does std::move copy contents for a rvalue or const lvalue function argument?为什么 std::move 复制右值或 const 左值函数参数的内容?
【发布时间】:2020-05-25 14:01:00
【问题描述】:

如果我在当前范围内的堆栈对象上使用 std::move,则内容将移动到目标,而源为空。

#include <iostream>
#include <string>
#include <utility>
#include <vector>

int main()
{
    std::string str("stackoverflow");

    std::vector<std::string> vec;
    vec.emplace_back(std::move(str));
    std::cout << "vec[0]: " << vec[0] << std::endl;

    std::cout << "str: " << str << std::endl;
}

结果:

vec[0]: stackoverflow
str: 

如果我将 std::move 用于右值或 const 左值函数参数,则会复制内容。

#include <iostream>
#include <memory>
#include <vector>
#include <utility>

void process_copy(std::vector<int> const & vec_)
{
    std::vector<int> vec(vec_);
    vec.push_back(22);
    std::cout << "In process_copy (const &): " << std::endl;
    for(int & i : vec)
        std::cout << i << ' ';
    std::cout << std::endl;
}

void process_copy(std::vector<int> && vec_)
{
    std::vector<int> vec(vec_);
    vec.push_back(99);
    std::cout << "In process_copy (&&): " << std::endl;
    for(int & i : vec)
        std::cout << i << ' ';
    std::cout << std::endl;
}

int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    process_copy(std::move(v));

    std::cout << "In main: " << std::endl;
    for(int & i : v)
        std::cout << i << ' ';
    std::cout << std::endl;
    std::cout << "size: " << v.size() << std::endl;
}

结果:

In process_copy (&&): 
0 1 2 3 4 5 6 7 8 9 99 
In main: 
0 1 2 3 4 5 6 7 8 9 
size: 10

为什么 std::move 的行为不同?

【问题讨论】:

  • 请记住,std::move 实际上并没有移动任何东西。它只是一个 cast 到右值引用。该右值引用随后是否被移动或复制取决于上下文以及对象是否实现移动操作以及源对象是否为const
  • 在第一个例子中,actually moving the ownership of the data 是 std::string 的移动构造函数。在第二个示例中,您只是发送一个右值 ref。您可以在此处手动获取传递的 std::vector 的所有权。

标签: c++ move-semantics


【解决方案1】:

如果值绑定到变量,即使它被声明为右值引用 (&amp;&amp;),您也需要使用 std::move。即它应该是:

void process_copy(std::vector<int> && vec_)
{
    std::vector<int> vec(std::move(vec_));
    ...
}

【讨论】:

  • 补充一点,需要注意的是,真正转移数据所有权的是移动构造函数。
  • 在这种情况下不应该使用 std::forward 来提供更好的可读性吗?
  • @Klaus vec_ 不是转发参考,所以我会说move 更适合。
  • @Klaus 确实 std::forward 也可以工作,但我们应该了解 std::forward 的目的,即“完美转发”。这里 vec_ 是一个右值引用,所以最好直接使用 std::move,这是为此而生的。
  • @theWiseBro:好的,明白了!谢谢!
【解决方案2】:

您的矢量实际上是复制的,而不是移动的。原因是,尽管声明为右值引用,vec_ 表示函数体内的 lvalue 表达式。因此,std::vector 的复制构造函数被调用,而不是移动构造函数。这样做的原因是,vec_ 现在是一个命名值,而右值不能有名称,所以它折叠为一个左值。由于这个原因,以下代码将无法编译:

void foo(int&& i)
{
    int&& x = i;
}

为了解决这个问题,您必须再次调用vec_ nameless,方法是调用std::move(vec_)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-23
    • 1970-01-01
    • 2021-10-07
    • 2023-04-07
    相关资源
    最近更新 更多