补充——右值引用、移动语义、完美转发

本章为C++11/14中右值引用、移动语义、完美转发的补充知识。(程序设计比赛和相关考试中一般不会用但是超时的时候可以提点速度试试,属于是选修课了...)

右值引用
C++引入了右值引用和移动语义,以避免无谓的复制,优化内存、提高程序性能。
①左值和右值
左值:指的是表达式结束后依然存在的持久化对象。
右值:指的是表达式结束时就不再存在的临时对象。
C++中所有的值必然属于左值、右值二者之一。
所有的具名变量或对象都是左值,而右值不具名。
区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值。

②左值引用、右值引用
C++11中的右值引用使用的符号是“&&”,如:
getTemp()返回的右值本来在表达式语句结束后,其生命也就该终结了(因为是临时变量),而通过右值引用,该右值又重获新生,其生命期将与右值引用类型变量a的生命期一样,只要a还活着,该右值临时变量将会一直存活下去。实际上就是给那个临时变量取了个名字。
注意:这里a的类型是右值引用类型(int &&),但是如果从左值和右值的角度区分它,它实际上是个左值。因为可以对它取地址,而且它还有名字,是一个已经命名的右值。所以,左值引用只能绑定左值,右值引用只能绑定右值,如果绑定的不对,编译就会失败。
而常量左值引用可以绑定非常量左值、常量左值、右值,而且在绑定右值的时候,常量左值引用还可以像右值引用一样将右值的生命期延长,缺点是【只能读不能改】。
总结,其中T是一个具体类型:
1)左值引用, 使用T&,只能绑定左值。
2)右值引用,使用T&&,只能绑定右值。
3)常量左值,使用const T&,既可以绑定左值也可以绑定右值。
4)已命名的右值引用,编译器会认为是左值。
5)编译器有返回值优化,但不要过于依赖。

移动语义之move
c++11中的所有容器都实现了move语义,move只是转移了资源的控制权,本质上是将左值强制转化为右值使用,以用于移动拷贝或赋值,避免对含有资源的对象发生无谓的拷贝。move对于拥有如内存、文件句柄等资源的成员的对象有效,如果是一些基本类型,如int和char[10]数组等,如果使用move,仍会发生拷贝(因为没有对应的移动构造函数),所以说move对含有资源的对象说更有意义。

完美转发
所谓转发,就是通过一个函数将参数继续转交给另一个函数进行处理,原参数可能是右值,可能是左值,如果还能继续保持参数的原有特征,那么它就是完美的。C++中可以借助universal references通用引用类型和std::forward()模板函数共同实现完美转发。(具体可见链接https://blog.csdn.net/locahuang/article/details/118755897)

(常用)容器中减少内存拷贝和移动(常用)
①使用vector容器一般都会用push_back()添加元素,这容易发生无意义的拷贝,解决办法就是为类增加移动拷贝和赋值函数,但其实可以直接使用emplace_back()函数代替push_back()。
emplace_back()可以直接通过构造函数的参数构造对象,但前提是要有对应的构造函数。在上面的代码中,也可以看到由于减少了拷贝,emplace_back()确实耗时小于push_back()。
②对于map和set,可以使用emplace()代替insert()。