《深入理解C++11》读书笔记——右值引用

左值右值的定义
左值:在作用域内可以持有的变量
右值:在下一行即消亡的临时变量

右值引用:可以持有即将消亡的临时变量的引用,用 T&& ref_name 表示
假设存在int i以下代码不会编译不出错的是 (1)(3)(4)
(1) int && ref = i * 3
(2) int & ref = i * 3 // 非常引用不能引用未声明的临时变量,未声明的临时变量属于常量
(3) const int & ref = i * 3 // 常引用
(4) int & ref = i
(5) int && ref = i // i是左值,右值引用不能引用

右值引用相关应用:
1.避免返回时,临时变量的创建和拷贝
在标准的C++98实现中T b = RetrunValue()返回过程中会发生两次拷贝,一次发生在return前,局部变量拷贝到临时变量,另一次发生在临时变量拷贝给b。
为了避免临时变量拷贝。
在C++98中经常采用“常引用”引用临时对象:
定义函数 T ReturnValue()
const T& b = RetrunValue();
在C++11可以使用右值引用
T&& b = RetrunValue();

实时上,当函数返回直接返回临时变量时,编译器对返回变量做了优化,将接收返回值变量直接创建在函数的临时变量的栈上,从而避免了临时变量的创建;这就是编译器的返回优化(RVO)或者NRVO
https://www.ibm.com/developerworks/community/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/RVO_V_S_std_move?lang=en
g++ 编译器加上-fno-elide-constructors选项去掉编译器优化

2.std::move
该函数的主要作用是将一个左值,变成右值,这个操作有风险,在使用std::move()将左值转换为右值之前,应确保左值不会被继续使用否则会引发异常。
原封不动返回函数内的临时变量对象,没有函数内的返回前拷贝,也没有函数外的临时变量拷贝(上面举的例子只是避免了函数外的临时变量拷贝)

T && ReturnValue()
{
	T obj;
	return std::move(obj); // obj的生命周期即将结束,此时将其转右值安全
}

T&& obj = ReturnValue(); // 此处obj和函数内的obj是同一个对象
还有一个典型的应用场景是实现高性能的Swap函数

template <class T>
void swap(T& a, T&b)
{
	T tmp(move(a));
	a = move(b);
	b = move(tmp);
}

3.移动构造函数
所谓移动构造函数就是一个接受右值的构造函数,好处对于资源管理的对象,移动构造函数可以避免了对象内的大内存拷贝(譬如堆上的内存)。

class HugeObj
{
	HugeObj(){};
	HugeObj(const HugeObj& obj){};
	// 移动构造函数
	HugeObj(HugeObj&& obj):ptr(obj.ptr) // 截取堆内存
	{
   		obj.ptr = NULL;
	}
	~HugeObj()
	{
  		delete ptr;
	}
private:
	HugeObjPtr* ptr;
}

注意到在C++11的环境下,函数return对象时或者有临时变量时,如果定义了移动构造函数,优先会调用移动构造函数其次才是拷贝构造函数

4.引用折叠
所谓引用折叠一句话概括就是在模板函数下,传递右值总是按右值引用处理,传递左值则按左值引用处理

5.完美转发

你可能感兴趣的:(C++)