C++的表达式要么是左值,要么是右值。
简单说,左值可以位于赋值语句的左边,右值则不能。
需要用到左值:
#include <iostream> //函数重载,形参为左值引用,接受左值 void process_value(int& i) { std::cout << "LValue processed: " << i << std::endl; } //函数重载,形参为右值引用,接受右值 void process_value(int&& i) { std::cout << "RValue processed: " << i << std::endl; } int main() { int a = 0; process_value(a);//传入左值,输出:LValue processed: 0 process_value(1);//传入右值,输出1:RValue processed: 1 }
左值引用&:不能将一个左值引用绑定到要求转换的表达式、字面常量或者返回右值的表达式
右值引用&&:某个对象的另一个名字,可以将一个右值引用绑定到要求转换的表达式、字面常量或者返回右值的表达式,不能将右值引用直接绑定到一个左值上,右值引用指向将要被销毁的对象,用右值引用接管所引用的对象的资源
int i = 1; int &&p = i;//错误,不能将一个右值引用绑定到一个左值上 int &q = i * 22;//错误,不能将左值引用绑定到返回右值的表达式,i * 22是一个右值 int &&q2 = i * 22;//正确,右值引用可以绑定到乘法右值结果上
可以把左值引用绑定到返回左值的表达式(返回左值引用的函数、赋值、下标、解引用、前置递增递减运算符)
int i = 1; int &r = i;//正确,左值引用
可以把const左值引用或者右值引用绑定到返回右值的表达式(返回非引用类型的函数、算术、关系、位、后置递增递减运算符)
const int &q1 = i * 22;//正确,可以将const引用绑定到右值
p是右值引用,指向一个右值,p本身是左值
include <iostream> //函数重载,形参为左值引用,接受左值 void process_value(int& i) { std::cout << "LValue processed: " << i << std::endl; } //函数重载,形参为右值引用,接受右值 void process_value(int&& i) { std::cout << "RValue processed: " << std::endl; } int main() { int a = 0; process_value(a);//输出:LValue processed: 0,说明传入的是左值 int&& p = 3; process_value(p); //输出:LValue processed: 3,说明传入的是左值 } //以上例子说明p是右值引用,指向一个右值,p本身是左值
不能将一个右值引用绑定到一个右值引用类型的变量上
变量是左值,不能将一个右值引用直接绑定到一个变量上
int &&p = 11;//正确,字面常量是右值 int &&q = p;//错误,p是左值
右值引用的意义
为临时变量续命,也就是为右值续命,因为右值在表达式结束后就消亡了,如果想继续使用右值,那就会动用昂贵的拷贝构造函数。
右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 的性能。
转移语义是和拷贝语义相对的,可以类比文件的剪切与拷贝,当我们将文件从一个目录拷贝到另一个目录时,速度比剪切慢很多。
头文件utility
使用move函数将获得一个绑定到左值上的右值引用类型
int &&p = 11;//正确,字面常量是右值 int &&q = p;//错误,p是左值 int &&q = std::move(p);//正确,move返回给定对象的右值引用
对于move不提供using声明,必须写成std::move,不能写成move
头文件utility
与move不同,forward必须通过显式模板实参来调用
forward返回该显式实参类型的右值引用,即forward返回的是T&&
template <typename F, typename T1, typename T2> void flip(F f, T1 &&t1, T2 &&t2) { f(std::forward<T2>(t2), std::forward<T1>(t1)); } //如果调用flip(g, i, 42),i将以int&类型传递给g,42将以int&&类型传递给g