Loading

std::move实现详解

参考

  1. std::move实现详解
  2. effective morder c++: 理解auto

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::movereturn语句中的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++中的引用折叠功能:

  1. 如果我们要调用的函数的参数为右值引用类型,并且该"右值引用类型"为模板参数类型,如果此时传递一个左值到函数,编译器会推断模板参数的实际类型为"左值引用类型"。
  2. X& &X&& &X& &&都会被折叠为X&
  3. 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什么也没做,直接返回。

posted @ 2023-09-15 14:53  Mogul_Kahn  阅读(875)  评论(0)    收藏  举报