移动语义4: 手动的右值
题目描述
在有些时候,我们会定义一些临时变量,其中的值只作暂时储存用,而无需长时间保留。例如在如下这段代码:
void swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
它实现了两个类类型变量的交换,但其中调用了1次复制构造函数和2次复制赋值运算符重载。但我们发现,这里用作传递的三次"="的变量仅作为临时储存使用,无需进行真正的"复制",也即无需将初始化或赋值操作的右侧操作数的值保留下来,即可以将值“移动”过去。
但这里的 a
、b
和 tmp
都是左值表达式,无法调用移动构造函数或移动赋值运算符重载。在这种情况下,我们可以通过 std::move
函数强制将左值转换为右值,从而调用移动构造函数和移动赋值运算符重载。(注意:这种操作较为危险,需要想清楚这一操作是否真的可以被“移动”)
完善 swap
函数的实现,使得在合适的时候尽可能使用移动语义。
关于输入
无
关于输出
见样例输出
参考答案
#include <iostream>
#include <cassert>
using std::cout, std::endl;
class A {
private:
int val;
public:
static int constructorCount;
A(int x) {
constructorCount++;
val = x;
}
// 本题不应调用复制构造函数和复制赋值运算符
A(A& x) = delete;
A& operator=(A& x) = delete;
A(A&& x) {
val = x.val;
x.val = 0; // 将x.val赋0模拟"移动"语义
}
A& operator=(A&& x) {
val = x.val;
x.val = 0; // 将x.val赋0模拟"移动"语义
return *this;
}
int getVal() const {
return val;
}
};
int A::constructorCount = 0;
void swap(A& a, A& b) {
A tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
int main(){
A a(1), b(2);
swap(a, b);
cout << a.getVal() << " " << b.getVal() << endl;
// 整份代码不应当再额外调用构造函数
assert(A::constructorCount == 2);
}