如何确保使用移动构造函数
How to ensure the move constructor is used
下面的代码给出了错误:
use of deleted function ‘constexpr B::B(const B&)’
现在,我知道发生这种情况是因为通过指定移动构造函数(有意)隐式删除了复制构造函数,并且复制向量会导致调用(已删除的)复制构造函数。我想我也理解为什么要使用向量的复制构造函数和赋值运算符。不过,我显然想使用移动构造函数和赋值运算符:移动对象,因此也移动它包含的向量。那么,如何让我的移动 constructor/assignment 运算符使用向量的移动 constructor/assignment 运算符?
代码如下:
#include <vector>
class B {
private:
/* something I don't want to copy */
public:
B() {};
B(B&& orig) {/* move contents */};
B& operator=(B&& rhs) {
/* move contents */
return *this;
};
};
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(orig.vec) {};
A& operator=(A&& rhs) {
vec = rhs.vec;
return *this;
};
};
只需对要从中移动的表达式中的向量调用 std::move
:
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(std::move(orig.vec)) {};
// ^^^^^^^^^
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
// ^^^^^^^^^
return *this;
};
};
即使您接受了右值引用,rhs
和 orig
在函数中仍然是左值,因此您需要对它们调用 std::move
。
为确保"move"构造函数和赋值运算符被调用,您需要提供一个正确值类别的对象。值类别用于确定可以使用哪些运算符和构造函数。
使用 std::move
to change the value category(在这种情况下从左值)到 xvalue(右值,可以从中移动)。
// ...
A(A&& orig) : vec(std::move(orig.vec)) {};
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
return *this;
};
move
不会以任何方式复制或更改对象,它只是转换为参数类型的右值引用 - 因此修改了值类别。
如果您的 class 的所有成员都是 classes 的对象,并且正确定义了 move constructor/assignment 运算符,您最好使用 default move constructor/assignment 运算符为您的 class。这将更容易,更不容易出错,并确保调用 class 成员的 move constructor/assignment 运算符。
在您的特定示例中,它将是:
class A
{
private:
vector<B> vec;
public:
A() : vec() {};
A(A&&) = default;
A& operator=(A&&) = default;
};
下面的代码给出了错误:
use of deleted function ‘constexpr B::B(const B&)’
现在,我知道发生这种情况是因为通过指定移动构造函数(有意)隐式删除了复制构造函数,并且复制向量会导致调用(已删除的)复制构造函数。我想我也理解为什么要使用向量的复制构造函数和赋值运算符。不过,我显然想使用移动构造函数和赋值运算符:移动对象,因此也移动它包含的向量。那么,如何让我的移动 constructor/assignment 运算符使用向量的移动 constructor/assignment 运算符?
代码如下:
#include <vector>
class B {
private:
/* something I don't want to copy */
public:
B() {};
B(B&& orig) {/* move contents */};
B& operator=(B&& rhs) {
/* move contents */
return *this;
};
};
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(orig.vec) {};
A& operator=(A&& rhs) {
vec = rhs.vec;
return *this;
};
};
只需对要从中移动的表达式中的向量调用 std::move
:
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(std::move(orig.vec)) {};
// ^^^^^^^^^
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
// ^^^^^^^^^
return *this;
};
};
即使您接受了右值引用,rhs
和 orig
在函数中仍然是左值,因此您需要对它们调用 std::move
。
为确保"move"构造函数和赋值运算符被调用,您需要提供一个正确值类别的对象。值类别用于确定可以使用哪些运算符和构造函数。
使用 std::move
to change the value category(在这种情况下从左值)到 xvalue(右值,可以从中移动)。
// ...
A(A&& orig) : vec(std::move(orig.vec)) {};
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
return *this;
};
move
不会以任何方式复制或更改对象,它只是转换为参数类型的右值引用 - 因此修改了值类别。
如果您的 class 的所有成员都是 classes 的对象,并且正确定义了 move constructor/assignment 运算符,您最好使用 default move constructor/assignment 运算符为您的 class。这将更容易,更不容易出错,并确保调用 class 成员的 move constructor/assignment 运算符。
在您的特定示例中,它将是:
class A
{
private:
vector<B> vec;
public:
A() : vec() {};
A(A&&) = default;
A& operator=(A&&) = default;
};