C++11中的 std::move 右值引用转换实现原理

1. 从一个简单的例子开始

我们看一个例子,研究一下 C++ 是如何判断参数是左值引用还是右值引用的。

#include <iostream>
using namespace std;

void print(int& x){
	cout << "x is int&" << endl;
}

void print(int&& x){
	cout << "x is int&&" << endl;
}

int main(){
	int x = 0;
	print(x);				\\x is int&
	print(0);				\\x is int&&
	print(std::move(x));	\\x is int&&
	return 0;
}

运行结果如下:

x is int&
x is int&&
x is int&&

2. std::move 原理分析

那么,std::move 到底做了什么工作,把本应该是左值引用的变量 x 硬生生改成了右值引用?我把 STL 和 move 函数有关的源代码汇总如下:

#define _NOEXCEPT	throw ()

// TEMPLATE remove_reference
template<class _Ty>
	struct remove_reference
	{	// remove reference
	typedef _Ty type;
	};

// TEMPLATE FUNCTION move
template<class _Ty> inline
	typename remove_reference<_Ty>::type&&
		move(_Ty&& _Arg) _NOEXCEPT
	{	// forward _Arg as movable
	return ((typename remove_reference<_Ty>::type&&)_Arg);
	}

在我们的例子中,x 的数据类型是 int,下面的代码按照 STL 定义方式展开后到底是什么样子的。

int x;
move(x);

首先看template struct remove_reference,实际上 _Ty 就是 int,于是我们得到:

typename remove_reference<_Ty>::type    ====〉  int

用 x 取代 _Arg,于是上面那一大坨代码就简化成了:

	int&& move(int&& x) 
	{	
		return (int&&) x;
	}

我靠,本来以为有很复杂的技巧,原来就这么简单。move 的输入参数定义保证了输入参数 x 只能是右值引用,然后原样返回输出即可。

3. 自己写一个 move 函数试试

把开始的例子改写了一下,非常OK!

#include <iostream>
using namespace std;

void print(int& x){
	cout << "x is int&" << endl;
}

void print(int&& x){
	cout << "x is int&&" << endl;
}

int&& move(int&& x){
	return (int&&)x;
}

int main(){
	int x = 0;
	print(x);
	print(0);
	print(move(x));
	return 0;
}

本质上,std::move(x) 返回了变量的右值引用指针,移交给第三方随意处置。这意味着,x 在被使用之后,生命周期就结束了,之后任何对 x 的使用都是不可靠的。

把一个变量以右值引用方式传递给一个函数,等于对这个函数说,这是本变量最后一次为别人提供服务,你可以对我随意处置,之后不会再有任何地方会引用我的内容,我随时可能被析构掉。

发布了174 篇原创文章 · 获赞 80 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/quicmous/article/details/99705626