移动语义4: 手动的右值

题目描述

在有些时候,我们会定义一些临时变量,其中的值只作暂时储存用,而无需长时间保留。例如在如下这段代码:

void swap(T& a, T& b) {
    T tmp = a;
    a = b;
    b = tmp;
}

它实现了两个类类型变量的交换,但其中调用了1次复制构造函数和2次复制赋值运算符重载。但我们发现,这里用作传递的三次"="的变量仅作为临时储存使用,无需进行真正的"复制",也即无需将初始化或赋值操作的右侧操作数的值保留下来,即可以将值“移动”过去。

但这里的 abtmp 都是左值表达式,无法调用移动构造函数或移动赋值运算符重载。在这种情况下,我们可以通过 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);
}