一、概述
1、左值引用是对左值的引用,右值引用是对右值的引用;
2、左值引用的目的是防止函数在进行传参和返回值的时候进行对象的拷贝;右值引用是为了移动语义和完美转发
3、const左值引能指向右值;局限不能修改这个值
4、右值引用通过std::move(v)可以指向左值
5、声明出来的左值引用或右值引用都是左值
二、什么是引用
1、引用是别名
2、声明时必须初始化
3、通过引用修改变量
4、引用的本质是指针常量
三、区分左值和右值
1、左值可以在等号左边,能够取地址,具名;比如变量名、返回左值引用的函数调用、前置自增/自减、赋值运算符、解引用等
2、右值只能在等号右边,不能取地址,不具名;右值分为纯右值和将亡值,纯右值比如字面值、返回非引用类型的函数调用、后置自增/自减等
四、什么是将亡值
1、将亡值是C++11引入的一种新的引用类型,用于支持移动语义的实现;
2、在C++中,表达式具有值的属性,可以分为左值和右值。左值是可以标识的、持久的对象,而右值是临时的、匿名的、即将被销毁的对象。
3、将亡值是一种特殊的右值引用类型,表示一个即将要被移动的对象或资源。通过将亡值引用,可以实现高效的资源转移,而不需要进行深拷贝的操作。
4、将亡值引用的语法是通过在类型后加上 && 运算符来定义。例如,int&& 表示一个将亡值引用,可以绑定到一个右值。
5、将亡值引用通常在移动构造函数和移动赋值运算符中使用,用于从一个对象转移资源到另一个对象,而不需要进行资源的赋值操作。这样可以显著提高程序性能和效率。
总结:一个纯右值如果能触发移动构造函数或移动赋值构造函数,那它就是一个将亡值。
五、理解移动语义
1、移动语义旨在通过转移资源的所有权而不进行数据拷贝来提高程序的性能。
2、在C++11之前进行我们想要构造一个与A一样的B对象,我们需要重新分配一块内存,把A对象里面的资源拷贝到B里面,然后把A释放掉,这样就得到了一个与A一样的B对象。这是一个深拷贝的过程,将会造成资源的重新分配。C11就实现了一个移动语义,就不需要重写构造这个资源,而是复用这个资源,把这个资源迁移过来,通过移动构造和移动拷贝构造可以把A对象迁移为B对象,提高效率。
3、实现移动语义是为了解决对象赋值时,避免资源的重写分配;通过移动构造和移动拷贝构造触发;并且在STL、std::unique_ptr中应用广泛。
六、理解完美转发
void func(int& n)
{
cout << "lvalue=" << n << endl;
}
void func(int&& n)
{
cout << "rvalue=" << n << endl;
}
template <typename T>
void revoke(T&& t)
{
func(forward<T>(t));
}
1、函数模板可以将自己的参数完美地转发给内部调用的其它函数
2、完美是不仅能准确转发参数的值,还能保住被转发的参数的左右值属性不变
3、借用万能引用,通过引用的方式接受左右属性的值
4、左右值属性:右值属性可能会触发移动语义;左值属性会触发拷贝构造
5、引用折叠规则:参数为左值或左值引用,T&& 将转换为 int&
参数为右值或右值引用,T&& 将转换为 int&&
6、折叠引用只是解决了传入参数的问题,还有一个透传的问题,就是把它的左右属性保存不变。
7、std::forward< T >(v):T 为左值或左值引用,v将转化为 T 类型的左值
T为右值或右值引用, v将转换为 T 类型的右值
总结:完美转发两步
(1)通过万能引用,万能引用既可以接受左值/引用也可以接受右值/引用
(2)转发语义借助std::forward<T>(v)转发函数,这个转发会把这个T的类型引用给它去掉,光留下它值的属性值。如果它是一个左值引用我们就把它转化为左值,如果它是一个右值引用我们就把它转化为右值。