move与forward的作用
- move主要是为了将左值转换为右值
- forward主要是为了在模板
- move与forward实现均用到了remove_reference,它的作用就是把一个模板类型T中可能蕴含的
&
号(0个,一个或者两个)给去掉,留下原始不带引用的类型,不了解可以看看remove_reference作用与原理这一节
move
move原理
move就是把不论左值右值都强转为右值,也就是比如int&&
这种类型。
move之后表示传的这个参数,你后面不要再用了,或者说表示传的这个参数后面没人会用了,你想怎么样怎么样吧(所以我们在移动构造函数或是赋值运算符中可以把参数的这个右值引用随便修改,,当然也不是随便啦,通常比如我们把指针复制过来,原来的指针赋值为nullptr)
move具体实现
/**
* @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); }
- 首先参数
_Tp&& __t
,这个参数不管传左值右值,都是引用方式传(__t成为一个左值引用或者右值引用)- 具体来讲如果传给move的是左值
int&
类型,那么_Tp
就是int&
类型,int& &&
折叠后还是int&
类型 - 如果传给move的是右值
int&&
类型,那么_Tp
就是int&&
类型,int&& &&
折叠后还是int&&
类型 - 关于引用折叠可以看补充知识->引用折叠一节
- 具体来讲如果传给move的是左值
- 然后函数体就return一句,你了解了
remove_reference
就一定能看懂了,就是用static_cast进行了一个强转
forward
forward原理
本来当前函数在用T&&x
接收完参数之后,你想要再把x传给其它函数,但是此时x是一个有名变量,也就是说不管原来传进来的是一个左值右值,现在x都是一个左值了,其它函数再接收的话只会当成左值来接收。
所以就有了forward来进行完美转发,forward<T>(x)
返回T&&
类型,也就是说
- 如果给x的是左值
int&
,那么T&&
就是int& &&
,折叠后也就还是int&
- 如果给x的是右值
int&&
,那么T&&
就是int&& &&
,折叠后也就还是int&&
所以说下面这两个代码基本等价
template<typename T>
void f1(T&& t) {
f2(static_cast<T>(t));
}
template<typename T>
void f1(T&& t) {
f2(forward<T>(t));
}
细小区别就是,forward版本中,验证了t和T是否一致,它不允许int&&类型被转为int&类型,即下面的写法运行会报错
int x = 3;
forward<int&>(move(x));
// or
forward<int&>(3);
我们通常也不应该这么用forward。。有一个T&&t
的定义,然后就用forward<T>(t)
转发就好
forward具体实现
/**
* @brief Forward an lvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
/**
* @brief Forward an rvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
参数就是原始的类型的左值或者右值引用,这样就根据重载调用某一个具体函数,然后再强转为_Tp&&
的引用类型返回
补充知识
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; };
很显然就是针对不同类型,T,T &,T &&,分别重载,定义出只返回不带引用符号的T
引用折叠
引用折叠是出现在模板参数是T&& t
或者T& t
的情况
-
T& t
情况,如果传来的是左值int&
,那么T就是代表int&
,int& &
折叠后还是int&
;如果传来的是右值,那么T就是代表int&&
,int&& &
折叠后是int&
,此时会报语法错误,因为右值不能赋值给左值引用 -
T&& t
情况,如果传来的是左值int&
,那么T就是代表int&
,int& &&
折叠后还是int&
;如果传来的是右值,那么T就是代表int&&
,int&& &&
折叠后还是int&&
,所以T&& t
可以说是继常量左值引用const T&
后的又一万能参数,而且我们可以修改t
的值,并且完美转发t