std::move实现详解
参考
std::move的定义
移动函数std::move定义在头文件<utility>之中。
下面的定义截取自gcc源码:
/**
* @brief Convert a value to an rvalue.
* @param __t A thing of arbitrary type.
* @return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
从大体上看,我们可以看到std::move()是一个模板函数,函数的传参为_Tp&& __t,返回右值引用typename std::remove_reference<_Tp>::type&&。其注释说明了以下几点信息:
- 此函数用于将一个值转换为一个右值(注意这里没有强调被转换的值是“左值”还是右值,仅说了是“值”)
__t是任意类型的参数- 函数会将参数
__t转化为一个右值引用,使其可以移动
std::remove_reference
std::remove_reference也是个函数模板,从名称上就比较好理解,即为移除引用。所以我们只想知道,它是如何移除引用的。
我们先来看看std::remove_reference的定义:
/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
再回头去看std::move的return语句中的std::remove_reference<_Tp>::type&&,结合remove_reference的定义。我们可以看到对于模板类型<_Tp>,不论我们传入的是此变量本身的类型,还是引用类型_Tp&,还是右值引用类型_Tp&&最终得到的都是std::remove_reference<_Tp>::type都是最原始的类型, 参考https://cppinsights.io/s/0d3b09d7。
所以std::remove_reference<_Tp>::type&&中的_Tp无论是原始类型、引用类型还是右值引用类型,最终得到的都是一个右值引用(注意最后的 &&)。
综合流程分析
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
本文没有介绍std::move的用法,但是为了能够继续讲解,其参数虽然定义为右值引用类型,但实际上可以接受的参数类型有三种(即原始类型,引用类型和右值引用类型都可以),而返回值的类型则始终是右值引用类型。下面我们逐个解释:
为了说明,这里引入c++中的引用折叠功能:
- 如果我们要调用的函数的参数为右值引用类型,并且该"右值引用类型"为模板参数类型,如果此时传递一个左值到函数,编译器会推断模板参数的实际类型为"左值引用类型"。
X& &、X&& &和X& &&都会被折叠为X&。X&& &&会被折叠为X&&。
下面,假定类型T为原始类型,T&为其引用类型,T&&为其右值引用类型;参数为 t。
传入的类型为T
根据引用折叠的第一条规则,在std::move(t),模板类型_Tp将被推断为实参t的左值引用类型T&,形参__t的类型就是T& &&,根据折叠规则第二条即为T&。
然后由std::remove_reference<>摘掉T&的引用得到T,通过static_cast转换为T&&类型并返回。
从一个左值
static_cast到一个右值引用是允许的
通常情况下,static_cast只能用于其他合法的类型转换。但是,这里又有一条针对右值引用的特许规则:虽然不能隐式地将一个左值转换为右值引用,但我们可以用static_cast显式地将一个左值转换为一个右值引用。
传入的类型为T&
此时,调用std::move(t),参数t的类型为T&。
在std::move(t)内部,形参 __t 的类型是T&,即 _Tp&& 等价 T&。
根据引用折叠规则第2条,_Tp等价T&(因为T& &&才可折叠为 T&)。
然后由std::remove_reference<>摘掉T&的引用得到T,通过static_cast转换为T&&类型并返回。
引用折叠得到的最终结果是
_Tp的类型
传入的类型为T&&
此时,调用std::move(t),参数t的类型为T&&。
在std::move(t)内部,形参__t的类型是T&&,即_Tp&&等价T&&,推断出_Tp类型即为T。
然后由std::remove_reference<>直接会得到T,由于__t已经是T&&类型,所以static_cast什么也没做,直接返回。

浙公网安备 33010602011771号