一、左值和右值
C++的表达式要么是左值,要么是右值;可以位于等号左边的值叫做左值,不能位于等号左边的值叫做右值;
左值
可以放到等号左边(可以取地址并且有名字)的东西就是左值;
- 赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果仍然是一个左值;
- 取地址符作用于一个左值运算对象,返回指向该运算对象的指针,这个指针是一个右值;
- 内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符求值结果都是左值;
- 前置自增自减表达式++i、--i得到的都是左值;
- 函数名和变量名一般是左值;
右值
不可以放到等号左边(不能取地址的没有名字)的东西就是右值;右值分为纯右值和将亡值。右值可以用左值来代替,但是右值不能代替左值;用左值代替右值时,使用的是对象的内容(值);
纯右值
运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。
- 除字符串字面值外的字面值
- 返回非引用类型的函数调用
- 后置自增自减表达式i++、i--
- 算术表达式(a+b, a*b, a&&b, a==b等)
- 取地址表达式等(&a)
将亡值
将亡值是指C++11新增的和右值引用相关的表达式,通常指将要被移动的对象、T&&函数的返回值、std::move函数的返回值、转换为T&&类型转换函数的返回值,将亡值可以理解为即将要销毁的值,通过“盗取”其它变量内存空间方式获取的值,在确保其它变量不再被使用或者即将被销毁时,可以避免内存空间的释放和分配,延长变量值的生命周期,常用来完成移动构造或者移动赋值的特殊任务。
二、左值引用和右值引用
左值引用就是对左值进行引用的类型,右值引用就是对右值进行引用的类型;它们都是引用,都是对象的一个别名,并不拥有所绑定对象的堆存,所以都必须立即初始化。
int i = 10;
int &r = i; //左值引用
int &&rr = i * 10; //右值引用
左值引用(&)
对于左值引用,等号右边的值必须可以取地址,如果不能取地址,则会编译失败,或者可以使用const引用形式,但这样就只能通过引用来读取输出,不能修改数组,因为是常量引用。
右值引用(&&)
对于右值引用,等号右边的值需要是右值,可以使用std::move函数强制把左值转换为右值。
使用注意
右值引用类型变量是一个左值;因此右值引用无法和右值引用类型变量绑定;
左值引用类型变量是一个左值;不支持为右值建立非常量左值引用,但允许使用常量左值引用操作右值。也就是说,常量左值引用既可以操作左值,也可以操作右值。
- 非常量左值引用可以引用的值的类型只有非常量左值,常量左值引用非常量左值、常量左值及右值
int num1 = 10;
const int num2 = 100;
int& a = num1; //编译成功,非常量左值引用支持引用非常量左值
int& b = num2; //编译失败,非常量左值引用不支持引用常量左值
int& c = 10; //编译失败,非常量左值引用不支持引用右值
const int& d = num1; //编译成功,常量左值引用支持引用非常量左值
const int& e = num2; //编译成功,常量左值引用支持引用常量左值
const int& f = 100; //编译成功,常量左值引用支持引用右值
- 右值引用不支持引用左值;非常量右值引用可以引用的值的类型只有非常量右值,常量右值引用非常量右值、常量右值
int num1 = 10;
const int num2 = 100;
int&& a = num1; //编译失败,非常量右值引用不支持引用非常量左值
int&& b = num2; //编译失败,非常量右值引用不支持引用常量左值
int&& c = 10; //编译成功,非常量右值引用支持引用非常量右值
const int&& d = num1; //编译失败,常量右值引用不支持引用非常量左值
const int&& e = num2; //编译失败,常量右值引用不支持引用常量左值
const int&& f = 100; //编译成功,常量右值引用支持引用右值