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 referencetypedef _Ty type;};// TEMPLATE FUNCTION move
template<class _Ty> inlinetypename remove_reference<_Ty>::type&&move(_Ty&& _Arg) _NOEXCEPT{
// forward _Arg as movablereturn ((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 的使用都是不可靠的。
把一个变量以右值引用方式传递给一个函数,等于对这个函数说,这是本变量最后一次为别人提供服务,你可以对我随意处置,之后不会再有任何地方会引用我的内容,我随时可能被析构掉。