在学习C++ primer Plus的第八章“函数探幽”的过程中,我终于见到了以前一直不懂的“int && a”字样的语法结构。现在知道了,这个叫做“右值引用”,其特点就在于可以引用右值。那么究竟怎样理解“左值”和“右值”,以及“右值引用”呢?
首先,通过一个直观的例子了解什么是左值和什么是右值。
int a;
a = 1;
以上代码实现了对int型变量a的声明和赋值。其中赋值的部分可以理解为“将1赋予变量a”,在这里,等号的左侧是变量a,是“左值”,等号右侧的“1”是常量,是“右值”的一种形式。显然,这是一条合法的语句。那么,这个语句倒过来可以吗?答案是否定的。比如下面的代码:
1 = a;
a + b = c;
这样的语句,都是无法通过编译的,编译器显示“error: lvalue required as left operand of assignment”,所以,“左值”就是一定要放在等号左侧的,“右值”就是一定要放在等号右侧的。
一般而言,左值可以是变量(C++内置的变量或自己创建的变量类型,例如结构或者类等),或者指定的可以更改的内存块(比如通过new语句创建的没有名称的存放数据的内存块)。而右值是常量(例如const类型的量)或字面量(例如带双引号的字符串字面值或者直接数字)或者一些表达式(例如“a+1”、“x+y”等)。
当然,书中对“左值”、“右值”知识的介绍并不是停留在这里,而是为了进一步介绍C++语言特有的“将引用作为函数参数”这一功能时所需要注意的事项做一些准备。
将变量的值或指针作为函数的参数这一功能,C++语言与C语言基本相同。然而C++比C语言强大,就在于它还增加了“将引用作为函数参数”的功能。所谓“引用”,就是为被引用的变量取另一个名字,并对它进行操作。可以举一个简单例子如下:
int add_self (int & a) /// 传递引用变量
{
a = a + 1;
return a;
}
int add_self (int a) /// 传递变量的值
{
a = a + 1;
return a;
}
这个函数可以实现将一个变量加一再返回的功能。传递变量的引用与直接传递变量值的区别,相信大家都明白,就是前者直接在函数中使用被引用变量,而后者则在函数中取变量的拷贝值进行运算。在调用函数之后,如果函数参量表中没有“const”字样,则被引用的变量可能被函数更改;而直接传递变量值的函数则不会改变。
虽然“引用”这个功能很好用,但它也有着更严格的要求,即放入函数参量表中的参数必须与函数声明中的引用类型一致。比如说:
#include <iostream>
using namespace std;
void print (int & a);
int main()
{
int a = 100;
print(a);
return 0;
}
void print (int & a)
{
cout << "print a as reference: " << a << endl;
}
上面这个程序可以正常运行,因为主函数中调用该函数时使用了变量名“a”本身,所以运行正常。
但此时如果将“a”改为“a+1”,则编译时开始报错:
“error: invalid initialization of non-const reference of type 'int&' from an rvalue of ...”
即,若使用引用型参量表,则必须使用左值来初始化。
相比之下,直接传递变量值的方法反而更不容易受限制,如下:
#include <iostream>
using namespace std;
void print (int a);
int main()
{
int a = 100;
print (a);
print(a+1);
return 0;
}
void print (int a)
{
cout << "print a as reference: " << a << endl;
}
该程序对左值(a)和右值(a+1)都可以正常运行,输出 100 和 101 两个数字。
那么,如果将引用用于函数参量表,就没有办法使用右值了吗?
当然有办法。那就是使用“int && a” ,即“右值引用”。
#include <iostream>
using namespace std;
void print (int && a);
int main()
{
int a = 100;
print(a+1);
return 0;
}
void print (int && a)
{
cout << "print a as reference: " << a << endl;
}
该程序可以正常运行,输出“a+1”的值 101 。但同时,在这个程序中,如果将“print(a+1)”改为“print(a)”,编译将再次报错,“error: cannot bind 'int' lvalue to 'int &&'”,因为右值引用的函数只能使用右值。
另外,这种右值引用的方法,不能像左值引用一样对被引内容进行修改,因为右值都是常量或者表达式,只存在于暂时存储中,不可以重复调用。故右值引用仅仅是一种扩展函数适应性的功能,可以用于函数重载等功能当中。(依据我当前的知识水平而言。)