【问题标题】:c++ why is move/forward necessary in moving constructor [duplicate]c ++为什么在移动构造函数中需要移动/向前[重复]
【发布时间】:2020-04-26 21:48:06
【问题描述】:

在使用移动构造函数构造对象后,新对象应该“窃取”“源”对象的资源,然后使其处于不确定(但有效)状态。

例如:

#include <iostream>
#include <vector>

template <class T>
void print(const std::vector<T>& v)
{
    std::cout << "size = " << v.size() << " vector = ";
    for (const auto& x : v)
        std::cout << x << " ";
    std::cout << std::endl;
}

int main()
{
    std::vector<int> data(10, 3);
    std::cout << "data:" << std::endl;
    print(data);

    std::vector<int> data2(std::move(data));
    std::cout << "data2:" << std::endl;
    print(data2);
    std::cout << "data after moving:" << std::endl;
    print(data);

    return 0;
}

live on Coliru

据我所知,标准并没有在调用移动构造函数后指定data的内容,但是可以预期data的资源已被data2窃取。事实上,上面程序的输出表明:

data:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 
data2:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 
data after moving:
size = 0 vector = 

现在考虑对上述程序稍作修改:

#include <iostream>
#include <vector>

class A {
    std::vector<int> m_data;
public:
    A(std::vector<int>&& data) : m_data{data} { }
    const std::vector<int>& data() const { return m_data; }
};

template <class T>
void print(const std::vector<T>& v)
{
    std::cout << "size = " << v.size() << " vector = ";
    for (const auto& x : v)
        std::cout << x << " ";
    std::cout << std::endl;
}

int main()
{
    std::vector<int> data(10, 3);
    std::cout << "data:" << std::endl;
    print(data);

    A x{std::move(data)};
    std::cout << "x.data():" << std::endl;
    print(x.data());
    std::cout << "data after moving:" << std::endl;
    print(data);

    return 0;
}

live on Coliru

我对程序的输出感到惊讶:

data:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 
x.data():
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 
data after moving:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 

看起来矢量data 只是被复制到A::m_data 而不是被移动。

如果我将 A 的移动构造函数替换为

A(std::vector<int>&& data) : m_data{std::move(data)} { }

(见live on Coliru

或与

A(std::vector<int>&& data) : m_data{std::forward<std::vector<int>&&>(data)} { }

(见live on Coliru

那么程序的输出类似于第一个代码的输出

data:
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 
x.data():
size = 10 vector = 3 3 3 3 3 3 3 3 3 3 
data after moving:
size = 0 vector = 

换句话说,似乎std:movestd::forward 是有效调用A::m_data 的移动构造函数所必需的。 std::movestd::forward 都返回一个static_caststd::vector&lt;int&gt;&amp;&amp;,但是A 的移动构造函数的参数已经是一个右值。

为什么需要额外的std::movestd::forward

【问题讨论】:

  • A(std::vector&lt;int&gt;&amp;&amp; data) : m_data{data}, m_data2{data} {} 是完全合法的,所以第一件事最好不要抹杀data!!
  • 当你使用data作为表达式时,这个表达式的类型被调整为非引用类型std::vector&lt;int&gt;。没有&amp;,没有&amp;&amp;。编译器应该如何知道调用哪个构造函数?价值类别。 data 具有左值类别,不能在移动构造函数中绑定到 std::vector&lt;int&gt;&amp;&amp;。要更改您使用的值类别,请移动:std::move(data) 具有 xvalue 类别并且可以绑定到 std::vector&lt;int&gt;&amp;&amp;。这将是比复制 ctor 更好的匹配。如果这听起来令人困惑,请参阅 this question

标签: c++ move-semantics rvalue


【解决方案1】:

首先你应该知道A::A(std::vector&lt;int&gt;&amp;&amp; data) 表示数据是对std::vector&lt;T&gt; 的右值引用,只是表示引用而不是调用移动构造函数,

A(std::vector&lt;int&gt;&amp;&amp; data) : m_data{data} { } 只是调用m_data 的复制构造函数,因为data 本身是一个命名值,而不是A::A 范围内的临时r value,因此std::movestd::forward 是必要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-22
    • 2015-08-04
    • 2017-01-10
    • 1970-01-01
    • 2015-07-18
    • 2016-06-20
    • 2011-04-17
    • 2013-08-11
    相关资源
    最近更新 更多