标签:编译 语言 函数返回 返回值 生命周期 指针 pre div 函数参数
为了了解C++11的新特性右值引用,不得不重新认识一下左右值。学习之初,最快的理解,莫过于望文生义了,右值那就是赋值号右边的值,左值就是赋值号左边的值。在中学的数学的学习中,我们理解的是,左值等价于等号左边的值,右值等价于等号右边的值;当我们继续学习C语言时,等号=不再叫等号,盖头换面叫做赋值号;那么来到C++我们还能这么理解吗?答案是部分否定的。
假如你现在还是这样理解,那么请继续往下......
左值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[5] = {2.0, 5.0, 10.0, 12.0}; double c1 = cube(side); double c2 = cube(lens[2]); 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指向它。这些变量生命周期只在函数调用期间,此后编译器便会随意的删除。
请看下面的例子:
void swapr(int& a, int& b) // swapr()完成数的交换的功能 { int temp; temp = a; a = b; b = temp; } long a = 5, b = 6; swapr(a, b);
这里我们使用反证法,假设这个调用是木有错误的,那么这里的类型不匹配,因此编译器将创建两个int临时变量,将它们初始化为3和5,然后交换临时变量的内容,但是这只是交换了两个临时变量的值,a和 b并没有交换,这与swapr()函数完成的功能是相悖的。
总结来说,如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决方法是,禁止创建临时变量。因此这个调用也是错误的。如果函数调用的参数是右值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量
标签:编译 语言 函数返回 返回值 生命周期 指针 pre div 函数参数
原文地址:http://www.cnblogs.com/SimonKly/p/7873589.html