前言

为了了解C++11的新特性右值引用,不得不重新认识一下左右值。学习之初,最快的理解,莫过于望文生义了,右值那就是赋值号右边的值,左值就是赋值号左边的值。在中学的数学的学习中,我们理解的是,左值等价于等号左边的值,右值等价于等号右边的值;当我们继续学习C语言时,等号=不再叫等号,盖头换面叫做赋值号;那么来到C++我们还能这么理解吗?答案是部分否定的。

假如你现在还是这样理解,那么请继续往下......

C++中何为左值lvalue和右值rvalue?

左值lvalue:可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值。在C语言中,左值最初指的是出现在赋值语句左边的实体,但这是引入const之前的情况。now,常规变量和const变量都可视为左值,因为可通过地址访问它们。常规变量属于可修改的左值,const变量属于不可修改的左值。左值基本上和以前的认知没有太大的改变。

右值rvalue:字面常量(用括号括起来的字符串除外,因为它们表示地址)、包含多项的表达式以及返回值的函数(条件是该函数返回的不是引用)。

简单的区别左值和右值:
右值就是一个临时变量(后面将详细的解释),只有临时地址空间,左值有其地址空间,换句话说,使用取地址符&对某个值取地址,如果不能得到地址,则是右值!

例如:

int a = 0;

cout << &a << endl; // ok! lvalue

cout << &404;  // error! rvalue

对于前面提到的右值的特性:

1) 允许调用成员函数。

2) 只能被 const reference 指向。

3) 右值不能当成左值使用,但左值可以当成右值使用

临时变量

仅当函数参数为const reference时,临时变量才能与之匹配。

什么时候将创建临时变量呢?
如果引用参数是const,则编译器将在下面两种情况下生成临时变量:
1. 实参的类型正确,但不是左值
2. 实参的类型不正确,但可以转换为正确的类型

Example:

double cube(const double &ra)
{
return ra * ra * ra;
} double side = 3.0;
double* pd = &side;
double& rd = side;
long edge = 5L;
double lens[] = {2.0, 5.0, 10.0, 12.0}; double c1 = cube(side);
double c2 = cube(lens[]);
double c3 = cube(rd);
double c4 = cube(*pd); double c5 = cube(edge); // ra是临时变量
double c6 = cube(7.0); // ra是临时变量
double c7 = cube(side + 4.0); // ra是临时变量

参数side、lens[2]、rd和*pd都是有名称的、double类型的数据对象,因此不需要借助临时变量,可以为其创建引用。
然而edge虽然有名称但是类型却不正确,正好符合产生临时变量的情况2 “实参的类型不正确,但可以转换为正确的类型” ,double引用不能指向long。参数7.0和side + 4.0的类型都正确,但是木有名称,属于右值。在这些情况下,编译器都会生成一个临时匿名变量,并让ra指向它。这些变量生命周期只在函数调用期间,此后编译器便会随意的删除。

为何临时变量 or 右值 对const引用的限制时合理的呢?

请看下面的例子:

void swapr(int& a, int& b)    // swapr()完成数的交换的功能
{
int temp; temp = a;
a = b;
b = temp;
} long a = , b = ;
swapr(a, b);

这里我们使用反证法,假设这个调用是木有错误的,那么这里的类型不匹配,因此编译器将创建两个int临时变量,将它们初始化为3和5,然后交换临时变量的内容,但是这只是交换了两个临时变量的值,a和 b并没有交换,这与swapr()函数完成的功能是相悖的。

总结来说,如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决方法是,禁止创建临时变量。因此这个调用也是错误的。如果函数调用的参数是右值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量

  

05-08 08:37