C++ 右值引用

216 阅读3分钟

C++11之右值引用基础

A b;
//不管是左值引用还是右值引用,只要是引用就一定是别名,不包含任何的拷贝赋值操作
//所以a就是std::move(b)这个右值对象的引用,也就不会调用A的任何构造函数
A&& a = std::move(b);

C++11之右值引用(移动语义)

右值引用主要是为了避免内存内容的无谓的拷贝
示例一
class Container {
public:
    Container(int n) {
        ptr = new int[n];
        size = n;
    }

    //这里表示cc不是临时对象,不可以用ptr=cc.ptr,因为会造成同一个ptr的两次析构
    Container(const Container& cc) {
        ptr = new int[cc.size];
        size = cc.size;
        for (int i = 0; i < size; ++i) {
            ptr[i] = cc.ptr[i];
        }
    }

//这里表示mc是临时对象,所以可以用ptr=mc.ptr,直接设置mc.ptr=nullptr就不会出现两次析构
//移动构造函数可以设置mc.ptr=nullptr,而贝构造函数里不可以设置cc.ptr=nullptr,因为cc可以在拷贝构造函数之后仍然有可能会被使用,而临时对象我们确定一定不会再被使用
    Container(Container&& mc) {
        this->ptr = mc.ptr;
        this->size = mc.size;
        mc.ptr = nullptr;
        mc.size = 0;
    }

    ~Container() {
        delete[] ptr;
    }

private:
    int* ptr;
    int size;
};
Container a(10);
//调用拷贝构造函数
Container c = a;
//调用移动构造函数,因为都是右值
Container f1(Container(2));
Container f2 = Container(2);
//1、std::move只对含有资源的对象有意义
//2、std::move只是完成了左值到右值的强制转换的工作,但是整体上Container d(std::move(c))就变成了Container d(xx),xx为右值,于是就调用了移动构造函数
//3、对基本类型,由于没有对应的移动构造函数,所以会调用默认的拷贝构造函数;对于对象,如果定义了移动构造函数就会调用移动构造函数,否则就调用拷贝构造函数
Container d(std::move(c));
//返回的时候会调用移动构造函数
T func(){
	T a;
	return a;
}
示例二
class Object {
public:
    Object(const std::string& n, int s) {
        desc = n;
        size = s;
    }

    Object(const Object& cc) {
        desc = cc.desc;
        size = cc.size;
    }

    Object(Object&& mc) {
        desc = mc.desc;
        size = mc.size;
    }
private:
    std::string desc;
    int size;
};

class Container {
public:
    Container(int n) : object("nothing", n) {
        ptr = new int[n];
    }

    Container(const Container& cp) : object(cp.object) {
        ptr = new int[cp.object.getSize()];
        for (int i = 0; i < object.getSize(); ++i) {
            ptr[i] = cp.ptr[i];
        }
    }

    Container(Container&& mp) : object(std::move(mp.object)) {
        ptr = mp.ptr;
        mp.ptr = nullptr;
    }
private:
    int* ptr;
    Object object;
};
//1、右值引用本身是一个左值,所以mp,mp.ptr,mp.object都是左值
//2、mp.ptr是基本变量,直接赋值即可
//3、但是对于mp.object,只会调用Object的拷贝构造函数的,不会调用Object的移动构造函数,通过std::move(mp.object)将mp.object强制转化为右值,这样就会调用Object的移动构造函数
Container c=Container(1);

C++11之右值引用(完美转发)

1、发生自动类型推断时,如函数模板的类型自动推导或者auto关键字,&&才是一个通用引用
2、任何的附加条件,如const T&&,std::vector<T>&&等都会使之失效,这时候&&就不是通用引用类型了
3、通用引用的好处在于,如果它被一个左值初始化,它就是一个左值引用;如果被一个右值初始化,它就是一个右值引用
template<typename T>
void forward(T t){
	func(t);
}
上面的代码中,我们希望forward函数将传入参数类型原封不动地传递给func函数,即forward函数接收到左值,则func接收到左值,forward接收到右值,func接收到右值
修改如下
template<typename T>
void forward(T&& t){
	func(static_cast<T&&>(t));
}
这个时候,就完成了forward函数接收到左值,则func接收到左值,forward接收到右值,func接收到右值,在C++11中,static_cast<T&&>(t) 可以通过std::forward<T>(t)来替代