右值(Rvalue)引用
在新标准C++0x中,Rvalue 引用是一项重要的语言特性,我们可以通过操作符“&&”来声明一个右值(Rvalue)引用,原先在C++中使用“&”操作符声明的引用现在被称为左值(Lvalue)引用。左值引用和右值引用分别都是什么呢?左值引用是指那些单一表达式结束之后依然存在的持久对象,例如:obj,*ptr,prt[index],++x。右值引用是指那些表达式结束时(在分号处)就不复存在了的临时对象。例如:1729, x + y,std::string("meow"),x++。二者都是针对表达式而言,不是对象。其中,右值引用支持移动语义和完全转发,移动语义(Move Semantics)可以带来巨大的性能提升,而完全转发(Perfect Forwarding)可以让高度泛型代码的编写变的更容易。
(1)移动语义
拷贝的问题,助长了移动语义。拷贝是对源对象的复制操作,拷贝后的对象是独立存在的,这样对于源对象来说就是一种冗余,并且会有资源开销。尽管返回值优化(RVO) 和命名返回值优化(NRVO)在特定情况下可以对拷贝构造操作进行优化,这有助于减缓问题的严重性,但是它们不能够消除所有冗余的拷贝。下面是拷贝操作的示例:
上面的代码,通过多次对象拷贝来实现了数据的传递,这样的拷贝操作,无疑会带来资源浪费和效率的低下,特别当这些对象很大的时候。如何能解决拷贝带来的问题呢?下面是通过右值引用实现和上面相同的功能。
在这段重新实现的代码中,我们使用了一个move()函数来代替对象的赋值操作符“=”,move()只是简单地接受一个右值引用或者左值引用作为参数,然后直接返回相应对象的右值引用。这一过程不会产生拷贝(Copy)操作,而只会将源对象移动(Move)到目标对象。
拷贝(Copy)操作会使源对象出现多份,而移动(Move)只是位置变化。移动语义正是通过右值引用,实现把拷贝操作变为移动对象,这样不仅节省了资源,同时还能提高性能。
(2)完美转发
完美转发是为解决早期版本中“转发”问题而提出的。在以前的C++标准中,如果编写泛型函数,如实现通过函数outer()把参数传递给函数inner()的功能,就需要根据参数的个数、参数的类型重载很多个函数。如下图:
上面的代码是对outer()函数只包含一个参数、两种参数类型的重载。如果是多个参数、多种参数类型,那重载的函数就会更多了。“转发”问题不仅拖慢了编译速度,极大的影响了代码的可读性。在C++0x中,右值引用优雅的解决了这个问题。
完美转发让你能简单而清晰地只写一个模板函数就可以转发所有的参数给任意函数,不管它带几个参数,也不管参数类型是什么。而且参数的非常量/常量, lvalue/rvalue 属性都能得以保留,让你可以像使用 inner() 一样使用 outer() 。如下图:
在新标准C++0x中,使用右值引用,解决了在标准C++标准中无法解决的问题,提高了代码性能,使代码更加灵活、高效。
小结
在Visual C++ 2010 中,提供了对新标准C++0x的支持,本文从Lambda表达式、auto 关键字、static_assert 声明、右值(Rvalue)引用四个方面进行了详细介绍,这些特性增强了代码的简洁性,同时有助于提高代码效率和性能。不过这只是一个抛砖引玉的介绍,还有更多Visual C++ 2010在语言方面的新特性等待大家去发掘。