2011-11-1 wcdj
问题:
rvalue类型表达式可以有引用吗?
const int &iref = 100; printf("%d", iref);// ok, 100
rvalue类型表达式可以有non-const的引用吗?
标准参考:
C++ 03 3.10 Lvalues and rvalues P.56
1, Every expression is either an lvalue or an rvalue. 表达式的属性,非对象的属性。
6, An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue (this includes the explicit creation of an object using functional notation (5.2.3)). 临时对象是右值。
8.3.2 References
测试代码:
#include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { int i = 1; int &i_ref = i;// ok, lvalue printf("i = %d\n", i_ref); // check lvalue /* int *i_p = &i; unsigned int *u_i_p = &((unsigned int)i);// error, '&' requires l-value */ unsigned int &u_i_ref = (unsigned int)i;// error ? the temporary object is an rvalue printf("i = %u\n", u_i_ref); return 0; }
几种编译方法:
(1) gcc -lstdc++ -o ref ref.cpp
(2) g++ -o ref ref.c
(3) g++ -o ref ref.cpp
分别在不同的编译器下测试结果如下:
(1) gcc 3.3.4 没有报错输出
i = 1
i = 1
下面代码可以说明gcc 3.3.4中的问题:
#include <stdio.h> #include <stdlib.h> int main() { int i = 1; int& ref = i;// ok, lvalue printf("i = %d\n", ref); unsigned int &u_i_ref = (unsigned int)i;// ok, gcc 3.3.4 printf("i = %u\n", u_i_ref); u_i_ref = 2; printf("i = %u\n", u_i_ref); // 2 printf("i = %u\n", i); // 1 return 0; }
使用不当就会存在这样的漏洞:
本意是想通过引用,调用fun函数来修改变量i的值,但是实际上fun实参和形参已经不是一个变量,因此调用完fun函数后,变量i的值并没有改变。这样在业务逻辑上就可能出现问题。
#include <stdio.h> #include <stdlib.h> void fun(unsigned int &uiPara) { uiPara = 100;// modify } int main(int argc, char* argv[]) { int i = 1; int& ref = i;// ok, lvalue printf("i = %d\n", ref);// 1 fun((unsigned int)i); printf("i = %d\n", i);// 1 return 0; }
应对上述问题,在gcc 3.3.4中正确的做法应该是:
#include <stdio.h> #include <stdlib.h> void fun(unsigned int &uiPara) { uiPara = 100;// modify } int main() { int i = 1; int& ref = i;// ok, lvalue printf("i = %d\n", ref);// 1 fun((unsigned int)i); printf("i = %d\n", i);// 1, sorry, it's not we want // ok unsigned int j = 1; printf("j = %d\n", j);// 1 fun(j); printf("j = %d\n", j);// 100, that's ok! return 0; }
(2) gcc 4.1.2 报错
error: invalid initialization of non-const reference of type 'unsigned int&' from a temporary of type 'unsigned int'
改为:
const unsigned int &u_i_ref = (unsigned int)i;// ok const unsigned int &u_i_ref = (unsigned int)i_ref;// ok
(3) VS2010 报错
error C2440: 'initializing' : cannot convert from 'unsigned int' to 'unsigned int &'
改为:
const unsigned int &u_i_ref = (unsigned int)i;// ok const unsigned int &u_i_ref = (unsigned int)i_ref;// ok
结论:
强制类型转换会产生一个临时对象(右值),gcc3.3.4允许这个临时对象具有no-const的相应类型的引用。而gcc4.1.2和VS2010要求对右值的引用必须是const引用。安全的做法是:不要对临时对象使用引用。
int tmp = 100; unsigned int &iref = (unsigned int)tmp; // ok, gcc3.3.4