move
表示可以重用对象中的资源move
实质是将任何值强制转换为「右值」引用,使得「移动构造函数」可以匹配上。move
需要和「移动构造函数/移动赋值运算符」一起使用才可以起到减少拷贝的作用。
move
move(a)
的含义是:a
中的资源可以移动到其他对象中,被再利用。
例如:
vector<string> words;
string str = "123";
words.push_back(move(str));
cout << "after move: " << str;
// "after move: "
move
的原理:实质上是将类型强制转换为「右值引用 T&&
」类型。
move
需要配合「移动构造函数」或「移动赋值函数」一起使用:
struct Obj {
// 移动构造函数
Obj(Obj&& obj) {
member = move(obj.member); // 再调用成员的移动构造函数
}
// 移动赋值运算
Obj& operator=(Obj&&) {
member = move(obj.member);
return *this;
}
};
构造函数的类型
构造函数的类型:
// 默认构造函数
T() {}
// 复制构造函数
T(const T& t) {}
// 移动构造函数
T(T&& t) {}
左值和右值
从 C++11
开始,表达式可以按照两个维度进行划分:「是否拥有身份」和「能否被移动」。三种基本的表达式类型「亡值 xvalue
」,「左值 lvalue
」和「纯右值prvalue
」如下所示:
拥有身份 (泛左值) |
不拥有身份 | |
---|---|---|
可被移动 (右值) |
亡值xvalue |
纯右值prvalue |
不可被移动 | 左值lvalue |
- |
其中,「亡值」和「左值」合起来是「泛左值」;「亡值」和「纯右值」合起来是「右值」。
基本类型:
- 左值:
- 特点是:可以取地址;
- 左值可用于初始化「左值引用
T&
」,此时相当于为表达式对象关联新的名字。 - 常见的左值:字符串字面量,变量名称构成的表达式,
++a
,等
- 纯右值:
- 常见的纯右值:除字符串字面量以外的字面量,如
42, true, nullptr
- 注:
a++
是纯右值,而++a
是左值。因此++a
性能更高。
- 常见的纯右值:除字符串字面量以外的字面量,如
- 亡值:
std::move(x)
是亡值。
注:右值:可被移动的值是右值,「纯右值」和「亡值」都是右值。
- 右值可用于初始化「常量左值引用
const T&
」,此时对象的生命周期延长至作用域结束; - 右值可用于初始化「右值引用
T&&
」,此时对象的生命周期延长至作用域结束。
例子:字符串常量作为左值:
cout << &"123" << endl; // 0x100c17e90
auto & str = "123";
cout << &str << endl; // 0x100c17e90
例子:变量的表达式是左值:
struct A {
// 移动构造函数
A(A&& a) { ... }
};
void foo(A&& a) {
// 变量 a 是 A&& 类型的,但是
// 「变量的表达式」是「左值表达式」,需要使用 move 转换为右值
// 这样才能使用「右值构造函数」
A a2 = std::move(a);
}