1.右值跟左值由什么区别
左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不在存在的临时对象。
2.什么是右值?
在C++11中,右值由两个概念构成,一个是将亡值,另一个是纯右值。
纯右值:非引用返回的临时变量、运算表达式产生的临时变量、原始字面量、lambda表达式等。
将亡值:C++11新增的、与右值引用相关的表达式,比如,将要被移动的对象、T&&函数返回值、std::move返回值和转换为T&&的类型的转换函数的返回值。
3.如何区分右值?
所有具名变量或对象都是左值,而右值不具名。
便捷方法:能不能对表达式取地址,如果能,则为左值,否则为右值。
4.一些特性
无论声明左值引用还是右值引用,都需要立即初始化,因为引用类型本身不拥有绑定对象的内存,只是该对象的别名。
常量左值引用也可以用来做性能优化,因为常量左值引用是一个“万能”的引用类型,可以接收左值、右值、常量左值和常量右值。需要注意的是非常量左值引用只能接收左值。
T&& 不一定就是表示右值,它绑定的类型是未定的,既可能是左值也有可能是右值。
template<typename T>
void f(T&& param);
f(10); //10是右值
int x = 10;
f(x); //x是左值
如上例,param有时是左值,有时是右值,这表示param实际上是一个未定的引用类型(universal reference)。它必须被初始化,
如果&&被一个左值初始化就是左值;如果&&被一个右值初始化就是右值。
需要注意的是,只有当发生自动类型推断时,比如函数模板的类型自动推导,或auto关键字,&&才是一个universal reference。
template<typename T>
void f(T&& param); //universal reference,T的类型需要推导
class Test {
...
Test(Test&& rhs); //右值引用,已经定义了一个特定的类型,没有类型推断
...
}
void f(std::vector<T>&& param) //右值引用,在调用 f 前,T的类型已经确定了
void f(const T&& param) //右值引用,universal reference仅发生在T&&下,任何一点的附加条件都会使之失效
5.引用折叠
由于存在T&&这种未定的引用类型,当它作为参数时,有可能被一个左值引用或右值引用的参数初始化,这种 经过类型推导后的T&&类型,相比于右值引用(&&)会发生类型的变化,这种变化被称为引用折叠。
引用折叠规则:
(1)所有的右值引用叠加到右值引用上仍然还是一个右值引用。
(2)所有的其他引用类型之间的叠加都将变成左值引用。
左值和右值是独立于它的类型的,右值引用有可能是左值也可能是右值,编译器会将已命名的右值引用视为左值,而将未命名的右值引用视为右值。
int&& var1 = 0; //var1的类型是int&&,但是本身是一个左值
auto&& var2 = var1; //根据引用折叠规则(2),var2的类型是int&
下面在看一个例子:
int w1, w2;
auto&& v1 = w1; //v1是universal reference, 被左值初始化,所以最终是一个左值
decltype(w1)&& v2 = w2; //报错!v2是一个右值引用类型,无法被左值初始化
如何将一个左值赋给一个右值引用类型呢?使用std::move, 可以将一个左值转换为右值。