C++11(新?)特性之右值引用
#右值
不能放到赋值表达式左侧的值,例如:
- 字面量
1
,"Hello"
,'c'
- 返回值而非引用的函数调用
foo()
- 运算结果
a + b
右值不能被直接赋值给一个非常引用,只能先赋给一个变量再取引用
1 | int& ref = 9; // error,“invalid initialization of non-const reference of type int& from an rvalue of type int” |
上述特性在泛型函数重载时会带来一些问题,这导致我们不得不对有无 const 两种情况分别重载
1 | template <typename T,typename A1> |
#右值引用
顾名思义,就是对右值的引用
#增加右值引用的好处
增加了右值引用之后,我们具备了识别和重载右值的能力
#进一步消除不必要的复制
在 c++11 之前,临时变量的创建一直是难以处理的问题. 有时这些临时变量可以被编译器优化(例如返回值优化),但是这并不总是可行的,通常这会导致高昂的对象复制成本.
考虑下面的代码:
1 |
|
理论上这里最多可能发生两次复制操作:
返回临时变量
vector 赋值
其中,第一次的复制操作可能会被编译器优化掉(RVO),然而第二次复制是不可避免的
当然我们可以通过其他方法来避免这次复制,比如通过指针或者传递一个已经填充好的 vector
有了右值引用之后,我们可以就可以对右值进行特殊化处理,从而避免多余的复制(移动构造函数和=运算符重载),例如
1 | // copy |
#移动语义的引入
可以显式指定某个资源被移交给另一个函数,自己不再需要
1 | Resource resource; |
对于有些不能被复制的资源(如std::unique_ptr
,std::thread
等等),这是很有用的
1 | std::string s; |
#模板的完美转发
引入右值后,出现了一个新问题,观察下面的代码:
1 | void bar(int& x) { std::cout << "左值引用\n"; } |
按照 C++ 的定义,当参数被传递到函数内部时,无论外部如何传递,函数内部的参数变量本身一定是具名的,因此被视为左值。那么就导致了:
- 右值的语义在函数传递过程中被丢失
- 右值需要被复制成左值(函数参数),有性能损耗
我们可以通过 std::forward
来实现完美转发,这指的是函数模板可以将自己的参数完好无损地转发给内部调用的其他函数中
1 | template <typename T> |
#实现
move
函数: 提醒编译器重载时选择移动构造函数
利用移动交换两个变量:
1 | void move_swap(Res &a,Res &b) |
RVO 和 NRVO
返回值优化(Return value optimization,缩写为 RVO)是 C++的一项编译优化技术。即删除保持函数返回值的临时对象。这可能会省略两次复制构造函数,即使复制构造函数有副作用。