左值
左值的概念
左值是一个内存块的表达,,代表一个地址,可以被取地址,赋值, 常常出现在赋值语句的左边.变量, 数组元素, 结构成员, 引用, 和解除引用都是左值.
左值常用的运算符
赋值运算符:
int a;
printf("%d", a = 4);
此处的a是一个左值, 当把4写入内存后, 整个表达式结果还是左值
(a = 4) = 5;
数组元素[]:
int arr[3] = {3, 5, 6};
arr[0]是一个左值, arr[0]可以被修改,写入
引用和解除引用的指针都是左值
vector<int>::iterator iter;
iter++, iter--
右值
右值的概念:
右值是不能通过地址访问的值,且右值不可被取地址, 这些值常常在寄存器上
常见的右值
"abc" 右值
16 右值
x + y 这种表达式是右值
常规函数的返回值也是右值
右值不可以写入
x + y = 3; // no
"abc" = "ghj"; // no;
10 = n; // no
引用的分类
左值引用
左值引用是是对左值进行引用的类别, 使用&符号进行声明, 它可以绑定一个左值.不可以引用右值
int a = 10; // a 是一个左值
int & value = a; // 左值引用
int & val = 100; // 不允许绑定右值
右值引用
右值引用是对右值进行引用的类型, 使用&&符号声明, 他可以绑定一个右值, 但不能绑定左值.
int && ref = 100; // 右值引用.
int a = 100;
int && value = 100; // 不允许绑定左值
const引用
const引用是对常量对象的引用, 使用const关键字进行声明, 可以绑定左值或右值,但不能修改所引用的对象.
int a = 100;
const int & value_1 = 100;
const int & value_2 = a;
const之所以可以绑定右值,是因为会生成临时对像
const int & a = 100;
这条语句等同于
int && temp = 100; // 先生成一个右值引用
const int & a = temp; // const引用绑定到右值
案例分析
++i, i++
++i, // 左值表达式, ++i先给变量加1, 然后返回本身, 所以是一个左值
(++i) = 5; // 可以被赋值
i++, // 先生成一个临时对象, 记录i的值, 之后在给i加1, 返回这个临时变量, 所以是一个右值
(i++) = 5; // 不可以被赋值
右值引用本身是左值
int && ref = 100; // 右值引用
int & value = ref // 左值引用
int && ref_2 = ref // 不可以将左值绑定到右值引用
对左值引用的修改, 会改变原来内存块的值, 右值引用则不会改变
int a = 100;
int & ref_2 = ++a;
ref_2 = 101; // 对ref_2的修改, 会导致变量a的改变
int && ref_2 = a++;
ref_2 = 101; // ref_2和a已经没有任何联系, 修改ref_2的值与a没有任何关系
重点强调
(1), 右值引用虽然引用了右值, 但其本身是一个左值
(2), 所有的变量都是一个左值, 因为他们有地址, 而且右值引用也无法绑定
(3), 任何函数形参都是左值, void func(int a, int && b) 虽然b是右值引用, 但是本身是一个左值
(4), 临时对象都是右值.
右值引用的引入目的
C++11引入右值引用的目的是为了支持移动语义和完美转发.
std::move()函数
std::move函数能够将一个左值强制转换成右值, 带来的结果就是可以将其绑定到右值上去.
int i = 10;
int && ref = std::move(i); // 将i转变成一个右值, 实现右值引用
ref = 20;
cout << ref << " " << i << endl; // 输出20 20
i = 29;
cout << ref << " " << i << endl; // 输出29 29
当将i绑定到ref后, 对ref的改变, 或者是对i的改变都会影响彼此.
int && ref_1 = 100;
int && ref_2 = std::move(ref_1); // 此时, ref_1 = 100, ref_2 = 100;
ref_1 = 200; // 此时, ref_1 = 200, ref_2 = 200;
ref_2 = 300; // 此时, ref_2 = 300, ref_1 = 300;
注意: std::move()函数没有移动功能.在移动完后,应该避免对源对象的使用.(就像上面的例子中, 使用了ref_1后,应该避免以后再次使用).