一个左值表达式代表的是 对象本身 ,而右值表达式代表的是 对象的值 ;变量也是左值。
为了支持移动操作(包括 移动构造函数和移动赋值函数 ),C++才引入了一种新的引用类型——右值引用,可以自由接管右值引用的对象内容。
返回非引用类型的函数,产生右值的表达式(算术表达式、关系表达式、位、后置递增递减)
清单1
1 int &&i = 1; 2 int b = 2; 3 cout << i << endl; 4 i = b; 5 cout << i << endl; 6 //输出1 2
清单1中i绑定到了右值1,说明:初始化时,右值引用一定要用一个右值表达式绑定;初始化之后,可以用左值表达式修改右值引用的所引用临时对象的值
;省略上面代码 int &&i = 1; 00C044DE mov dword ptr [ebp-14h],1 00C044E5 lea eax,[ebp-14h] 00C044E8 mov dword ptr [ebp-8],eax int b = 2; 00C044EB mov dword ptr [ebp-20h],2 ;省略输出代码 i = b; 00C04523 mov eax,dword ptr [ebp-8] 00C04526 mov ecx,dword ptr [ebp-20h] 00C04529 mov dword ptr [eax],ecx ;省略余下代码
清单2可以清楚看出右值引用的底层实现。第3行对一个临时变量赋值0,第4-5行将临时变量地址存储至i中,因此i存储的实际是临时对象的地址(也就是对临时对象的一种引用)。
第10行mov eax,dword ptr [ebp-8]取出了i存储的数据(临时对象地址),11-12行将b变量的值赋值到临时对象,而i存储的数据不变,仍是存储临时对象的地址(也就是对临时对象的一种引用)。
1 int&&temp =0; 2 int&&i = temp;
清单3中temp是一个右值引用变量,变量是左值,因此i引用temp左值是非法的!
1 int &&temp = 0; 2 int &&i = 1; 3 i = temp;
清单4是合法的,取出temp所引用临时对象的值,再赋值给i所引用临时对象的值。
1 class A{ 2 public: 3 int b; 4 int a; 5 char c[256]; 6 }; 7 void f(A a) 8 { 9 ; 10 } 11 int main(int argc, char const *argv[]) 12 { 13 A &&a = A(); 14 f(a); 15 }
清单5是合法的,过程符合一般逻辑,即会生成a引用临时对象的一个副本,再传入副本对象的地址。如果f函数的形参是引用类型的,那么调用f函数传入的是a引用对象的地址,而不会生成副本