左值引用与右值引用
一、左值引用与右值引用的区别
1.左值引用是对左值的引用;右值引用是对右值的引用(不过也有特例)
const左值引用能指向右值,不过不能修改;右值引用通过std::move(v)可以指向左值
声明出来的左值引用或者右值引用都是左值(int& lref = n, int&& rref = 20, lref和rref都是左值)
2.左值引用与右值引用在功能上的差异
(1)左值引用:左值引用用来传参或者做函数返回值可以避免对象的拷贝
(2)右值引用:实现移动语义和完美转发
①移动语义:通过实现移动语义,对象赋值时,避免资源的重新分配(移动构造和移动赋值构造)
②完美转发:函数模版可以将自己的参数完美地转发给内部调用的其他函数
完美指的是不仅能准确地转发参数的值,还能保证被转发的参数的左右值属性不变
// 接受左值引用的函数
void func(int &n)
{
std::cout << "lvalue=" << n << std::endl;
}
// 接受右值引用的函数
void func(int &&n)
{
std::cout << "rvalue=" << n << std::endl;
}
// 模板函数,使用std::forward完美转发
template<typename T>
void revoke(T &&t)
{
// 函数模版的参数 -完美交给-> 内部的函数
// 参数的值 + 参数的左右值属性
func(std::forward<T>(t));
}
int main() {
int num = 10;
revoke(num); // 传入左值 lvalue=10
revoke(20); // 传入右值 rvalue=20
return 0;
}
借助T&&实现转发语义,引入了引用折叠规则:参数为左值或左值引用,T&&将转化为A&;参数为右值或右值引用,T&&将转化为A&&
std::forward<T>(v) T为左值引用类型,v将转化为 T类型的左值;T为右值引用类型,v将转化为 T类型的右值
二、区分左值和右值
1.左值:可以在等号左边,能够取地址,具名(下面举一些例子)
变量名、前置自增和自减(++i,--i)、解引用表达式
返回左值引用的函数调用
// 定义一个函数,返回vector<int>中指定位置元素的左值引用
std::vector<int>& getElement(std::vector<int>& vec, size_t index) {
return vec[index];
}
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 调用函数获取左值引用
// getElement(numbers, 2)这个函数调用就是左值
int& ref = getElement(numbers, 2);
赋值运算或复合赋值运算符表达式
// (i = 9)是左值
(i = 9) = 1000;
std::cout << "i:" << i << std::endl; // i:1000
// (i += 20)是左值
(i += 20) = 10000;
std::cout << "i:" << i << std::endl; // i:10000
2.右值:只能在等号右边,不能取地址,不具名;右值分为两大类,纯右值和将亡值
(1)纯右值:字面值、返回非引用类型的函数调用、后置自增和自减
算术表达式[a+b]、逻辑表达式[a&&b]、比较表达式[a==b, a >= b]
(2)将亡值:C++11新引入的与右值引用[移动语义]相关的值类型
将亡值用来触发移动构造或移动赋值构造,进行资源转移,之后将亡值将调用析构
std::move static_cast<T&&>(x) 可以将左值转化为将亡值(将左值转换为右值、使拷贝变为移动)