(如有错误,欢迎指正!) move的作用很简单,可以把左值和右值都转换为右值,比如vector已经有了右值的拷贝构造和赋值函数,目的是为减少临时对象的构造,提高效率。
本文的主旨不是为了探讨对象的优化问题,所以对于左值、右值、临时对象、explicit(比较有意思的关键字)不会一一分析。
一个右值变量本质还是一个左值,在传递的时候,会丧失它的右值属性,对于提供了左值和右值的接口来说,这样就丧失了本意。
对于下面的例子来说,只会匹配左值,但是是tmp.func(move(b))时,可以将左值转换为右值,这样就可以正确的匹配右值函数。
template<typename T> class test { public: void show(T && tmp)//右值 { cout<<"&&"<<endl; } void show(T & tmp)//左值 { cout<<"&"<<endl; } }; int main() { test<int> tmp; int &&b = 20; tmp.func(b); //tmp.func(move(b)); }贴上move源码,源码很短
template<typename _Tp> constexpr typename std::remove_reference<_Tp>::type&& //重要的一个函数 move(_Tp&& __t) noexcept { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }只有4行,重点关注2,4行
首先来看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; };很有意思,3个重载,分别对应值,左值引用,右值引用,但是不管是哪一个,返回的都是值,这是move做的第一步。
第二步:
move(_Tp&& __t) noexcept return static_cast<typename std::remove_reference<_Tp>::type&&>(__t);强转为右值引用,然后返回。
如果还不理解可以接着看 怎么返回右值引用呢?&&+值不就是一个右值引用吗,所以
T a T &a => remove_reference => T a T &&a接下来
typename std::remove_reference<_Tp>::type&& (a) == T &&a这里使用了static_cast进行了强转,这样不管是什么属性,最后都会转换为一个右值。
forward 可以在传递的过程中,保持变量的属性 直接看源码,forward有2个重载函数,分别接收左值和右值。 同时可以看到可以看到 remove_reference
template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept { return static_cast<_Tp&&>(__t); } template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept { return static_cast<_Tp&&>(__t); }但是这次remove_reference转换的类型不再是值,而是一个引用
T &a => remove_reference & => T &a T &&a => remove_reference && => T &&a再来看最后一行
return static_cast<_Tp&&>(__t);转换的过程应用了引用折叠,原理是这样的
T &a => static_cast<_Tp&&>(T &a);=> T&& &a => T &a T &&a => static_cast<_Tp&&>(T &&a);=> T&& &&a => T &&a至此,不管是左值还是右值,都能保持它本来的属性。 比如:
template<typename T> class test { public: void func(T && tmp) { //show(tmp); show(forward<T>(tmp)); } void func(T & tmp) { //show(tmp); show(forward<T>(tmp)); } void show(T && tmp) { cout<<"&&"<<endl; } void show(T & tmp) { cout<<"&"<<endl; } }; int main() { test<int> tmp; int a = 10; int &&b = 20; tmp.func(b); //但是如果是下面会怎么样呢? //tmp.func(forward<int>(a)); //这一句会匹配右值,为什么呢? }还记得第一场面试被面试官吊打的场景,从unique_ptr到右值引用,学无止尽,每日进步。
