C++ 0x标准出来有一段时间了,一直没时间看,导致最近看一些代码完全不明白是什么意思了,只好硬着头皮来看了。
这次先说一个简单的,右值引用。
关于引用,大家都很清楚了,只会做一标识,而不会拷贝对象,例如:int a = 0; int& b = a; 这个就是传统的引用,如今也称为左值引用,一般我们将引用用在函数返回值和参数传递上。现在0x标准出来了一个右值引用。为了区别左值引用,就变成右值引用了,用”&&“来表示。
左值引用和右值引用最大的却别是:右值引用可以绑定到一个临时的对象(右值)上,而左值引用不行。
int a = 0; int& nLvRef = a; // 左值引用 int&& nRvRef = int(); // 右值引用
上面的是例子是一个左值引用和右值引用的例子。再看下面的例子:
int& nLvRef = int(); // 左值引用, VS报错:非常量引用的初始值必须是左值 error C2440: “初始化”: 无法从“int”转换为“int &” int&& nRvRef = int(); // 右值引用
从而可见,我们把一个临时对象(右值)绑定大了一个右值引用上,而左值引用却不可以这样绑定。
右值引用可以绑定一个临时(匿名)的对象,而临时的对象没有必要保存下来,进行操作的时候我么你可以”移动(Move)”它,而不是拷贝一个副本下来,这样就可以减少拷贝副本所带来的开销。
例如我们有下面的例子:
void swap(int& a , int& b) { int temp = a; a = b; b = temp; } int _tmain(int argc, _TCHAR* argv[]) { int a = 1; int b = 2; swap(a, b); std::cout << a << " " << b << std::endl; system("pause"); return 0; }
结果我们都很清楚,a和b的值交换了,但是这里大家注意到,用一个临时对象来做中间变量,我们做了很多次的对象拷贝。
下来我们使用右值引用中移动的思想来改写这个swap函数,如下:
void swap(int& a , int& b) { int temp = std::move(a); a = std::move(b); b = std::move(temp); } int _tmain(int argc, _TCHAR* argv[]) { int a = 1; int b = 2; swap(a, b); std::cout << a << " " << b << std::endl; system("pause"); return 0; }
这里你可能没看到右值引用操作符,但是却用了一个std::move(),这个是VS标准库中自带的一个移动函数。
我们来用右值引用模拟一下这个标准函数(稍微吐槽下,MS慢慢也接受了boost等公众认可的东西了,不搞特殊化了,以MS当年的性格,绝对要单独搞一个另外名字的函数)。
template<typename T> T&& move(T&& a) { return a; } template<typename T> void swap(T& a , T& b) { int temp = move(a); // a被移动到temp,a被清空 a = move(b); // b被移动到a,b被清空 b = move(temp); // temp被移动到a,temp被清空 } int _tmain(int argc, _TCHAR* argv[]) { int a = 1; int b = 2; swap(a, b); std::cout << a << " " << b << std::endl; system("pause"); return 0; }
注意:这里是移动,并没有做拷贝,只是将对象移动了一下而已,你可以认为是同一个你还了不同编号的座位。
C++0x中的右值引用算是将引用这块的东西补全了,虽然左值引用也很好用,但是大家对他的效率以及临时对象的处理上不是很满意,而右值引用完美的解决了这个问题。
不过现在大家用的VS编辑器各不一致,想要用右值引用需要VS2010(包含)以上的版本,建议还是用VS2012吧,2010的支持不全面。GCC最先的4.7.3已经全面支持C++ 0x标准了。还是喜欢GCC的果断,而不像VS一样拖泥带水,今天支持一点,sp1再支持一点,纠结。
好了,这个东西这么好用,有必要的话建议大家升级下项目