【发布时间】: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;
}
据我所知,标准并没有在调用移动构造函数后指定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;
}
我对程序的输出感到惊讶:
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)} { }
或与
A(std::vector<int>&& data) : m_data{std::forward<std::vector<int>&&>(data)} { }
那么程序的输出类似于第一个代码的输出
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:move 或std::forward 是有效调用A::m_data 的移动构造函数所必需的。 std::move 和std::forward 都返回一个static_cast 到std::vector<int>&&,但是A 的移动构造函数的参数已经是一个右值。
为什么需要额外的std::move 或std::forward?
【问题讨论】:
-
A(std::vector<int>&& data) : m_data{data}, m_data2{data} {}是完全合法的,所以第一件事最好不要抹杀data!! -
当你使用
data作为表达式时,这个表达式的类型被调整为非引用类型std::vector<int>。没有&,没有&&。编译器应该如何知道调用哪个构造函数?价值类别。data具有左值类别,不能在移动构造函数中绑定到std::vector<int>&&。要更改您使用的值类别,请移动:std::move(data)具有 xvalue 类别并且可以绑定到std::vector<int>&&。这将是比复制 ctor 更好的匹配。如果这听起来令人困惑,请参阅 this question。
标签: c++ move-semantics rvalue