C++11:右值引用

左值、右值

  在C语言中,我们常常会提起左值 lvalue 、右值 rvalue这样的称呼。一个最为典型的判别方法就是,在赋值表达式中,出现在等号左边的就是 左值 ,而在等号右边的,则称为 右值。如:

int a = 1;
int b = 2;
int c = a + b;

  在这个赋值表达式中,c 就是一个左值,而 a + b 则是一个右值。

  不过 C++ 中还有一个被广泛认同的说法,那就是可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。那么这个加法赋值表达式中,&c 是允许的操作,但 &(a + b) 这样的操作则不会通过编译。因此 c 是一个左值,(a + b) 是一个右值。在C++ 11 中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。

  相对于左值,右值表示字面常量、表达式、函数的非引用返回值等。

右值、将亡值

  在理解 C++ 11 的右值前,先看看 C++ 98 中右值的概念:C++ 98 中右值是纯右值,纯右值指的是临时变量值、不跟对象关联的字面量值。临时变量指的是非引用返回的函数返回值、表达式等,例如函数int func()的返回值,表达式 a+b;不跟对象关联的字面量值,例如 true2”C” 等。

  C++ 11 对 C++ 98 中的右值进行了扩充。在 C++ 11 中右值又分为纯右值(prvalue,Pure Rvalue)和将亡值(xvalue,eXpiring Value)。其中纯右值的概念等同于我们在 C++ 98 标准中右值的概念,指的是临时变量和不跟对象关联的字面量值;将亡值则是 C++ 11 新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如返回右值引用 T&& 的函数返回值、std::move 的返回值,或者转换为 T&& 的类型转换函数的返回值。

  将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值。在确保其他变量不再被使用、或即将被销毁时,通过“盗取”的方式可以避免内存空间的释放和分配,能够延长变量值的生命期。

左值引用、右值引用

  左值引用是对一个左值进行引用的类型,右值引用则是对一个右值进行引用的类型。

  左值引用和右值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化 。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名

左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。

左值引用:

int &a = 2;       // 左值引用绑定到右值,编译失败, err
int b = 2;        // 非常量左值
const int &c = b; // 常量左值引用绑定到非常量左值,编译通过, ok
const int d = 2;  // 常量左值
const int &e = c; // 常量左值引用绑定到常量左值,编译通过, ok
const int &b = 2; // 常量左值引用绑定到右值,编程通过, ok

const 类型 &为 “万能”的引用类型,它可以接受非常量左值、常量左值、右值对其进行初始化。

右值引用,使用&&表示:

int && r1 = 22;
int x = 5;
int y = 8;
int && r2 = x + y;
T && a = ReturnRvalue();

  右值值引用通常不能绑定到任何的左值,要想绑定一个左值到右值引用,通常需要 std::move()将左值强制转换为右值,例如:

int a;
int &&r1 = c;             // 编译失败
int &&r2 = std::move(a);  // 编译通过

C++ 11 中引用类型以及其可以引用的值的类型总结如下:
image.png