C++11学习笔记:std::move和std::forward源码分析(一)

2015-01-27 06:07:57 · 作者: · 浏览: 10
std::move和std::forward是C++0x中新增的标准库函数,分别用于实现移动语义和完美转发。
下面让我们分析一下这两个函数在gcc4.6中的具体实现。

预备知识

    引用折叠规则:
    X& + & => X&
    X&& + & => X&
    X& + && => X&
    X&& + && => X&&
    函数模板参数推导规则(右值引用参数部分):
    当函数模板的模板参数为T而函数形参为T&&(右值引用)时适用本规则。
    若实参为左值 U& ,则模板参数 T 应推导为引用类型 U& 。
    (根据引用折叠规则, U& + && => U&, 而T&& <=> U&,故T <=> U& )
    若实参为右值 U&& ,则模板参数 T 应推导为非引用类型 U 。
    (根据引用折叠规则, U或U&& + && => U&&, 而T&& <=> U&&,故T <=> U或U&&,这里强制规定T <=> U )
    std::remove_reference为C++0x标准库中的元函数,其功能为去除类型中的引用。
    std::remove_reference ::type <=> U
    std::remove_reference ::type <=> U
    std::remove_reference ::type <=> U
    以下语法形式将把表达式 t 转换为T类型的右值(准确的说是无名右值引用,是右值的一种)
    static_cast (t)无名的右值引用是右值
    具名的右值引用是左值。注:本文中 <=> 含义为“即,等价于“。

    std::move


    函数功能
    std::move(t) 负责将表达式 t 转换为右值,使用这一转换意味着你不再关心 t 的内容,它可以通过被移动(窃取)来解决移动语意问题。

    源码与测试代码 [cpp] view plaincopy
    1. template
    2. inline typename std::remove_reference<_Tp>::type&&
    3. move(_Tp&& __t)
    4. { return static_cast ::type&&>(__t); } [cpp] view plaincopy
      1. #include
      2. using namespace std;
      3. struct X {};
      4. int main()
      5. {
      6. X a;
      7. X&& b = move(a);
      8. X&& c = move(X());
      9. } 代码说明
          测试代码第9行用X类型的左值 a 来测试move函数,根据标准X类型的右值引用 b 只能绑定X类型的右值,所以 move(a) 的返回值必然是X类型的右值。测试代码第10行用X类型的右值 X() 来测试move函数,根据标准X类型的右值引用 c 只能绑定X类型的右值,所以 move(X()) 的返回值必然是X类型的右值。首先我们来分析 move(a) 这种用左值参数来调用move函数的情况。模拟单步调用来到源码第3行,_Tp&& <=> X&, __t <=> a 。
          根据函数模板参数推导规则,_Tp&& <=> X& 可推出 _Tp <=> X& 。
          typename std::remove_reference<_Tp>::type <=> X 。
          typename std::remove_reference<_Tp>::type&& <=> X&& 。再次单步调用进入move函数实体所在的源码第4行。static_cast ::type&&>(__t) <=> static_cast (a)根据标准 static_cast (a) 将把左值 a 转换为X类型的无名右值引用。然后我们再来分析 move(X()) 这种用右值参数来调用move函数的情况。模拟单步调用来到源码第3行,_Tp&& <=> X&&, __t <=> X() 。根据函数模板参数推导规则,_Tp&& <=> X&& 可推出 _Tp <=> X 。
          typename std::remove_reference<_Tp>::type <=> X 。
          typename std::remove_reference<_Tp>::type&& <=> X&& 。再次单步调用进入move函数实体所在的源码第4行。static_cast ::type&&>(__t) <=> static_cast (X())根据标准 static_cast (X()) 将把右值 X() 转换为X类型的无名右值引用。由9和16可知源码中std::move函数的具体实现符合标准,
          因为无论用左值a还是右值X()做参数来调用std::move函数,
          该实现都将返回无名的右值引用(右值的一种),符合标准中该函数的定义。

          不光是临时变量,只要是你认为不再需要的数据,都可以考虑用std::move移动。

          比较有名的std::move用法是在swap中:

          复制代码
          1 template
                             
                              
          2 void swap(T& a, T& b)
          3 {
          4 T t(std::move(a)); // a为空,t占有a的初始数据
          5 a = std::move(b); // b为空, a占有b的初始数据
          6 b = std::move(t); // t为空,b占有a的初始数据
          7 }
          复制代码

          总之,std::move是为性能而生的,正式因为了有了这个主动报告废弃物的设施,所以C++11中的STL性能大幅提升,即使C++用户仍然按找旧有的方式来编码,仍然能因中新版STL等标准库的强化中收益。



          std::forward


          函数功能
          std::forward (u) 有两个参数:T 与 u。当T为左值引用类型时,u将被转换为T类型的左值,否则u将被转换为T类型右值。如此定义std::forward是为了在使用右值引用参数的函数模板中解决参数的完美转发问题。

          源码与测试代码 [cpp] view plaincopy
          1. /// forward (as per N3143)
          2. template
          3. inline _Tp&&
          4. forward(typename std::remove_reference<_Tp>::type& __t)
          5. { return static_cast<_Tp&&>(__t); }
          6. template
          7. inline _Tp&&
          8. forward(typename std::remove_reference<_Tp>::type&& __t)
          9. {
          10. static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
          11. " substituting _Tp is an lvalue reference type");
          12. return static_cast<_Tp&&>(__t);
          13. } [cpp] view plaincopy
            1. #include
            2. using namespace std;
            3. struct X {};
            4. void inner(const X&) {cout << "inner(const X&)" << endl;}
            5. void inner(X&&) {cout << "inner(X&&)" << endl;}
            6. template
            7. void outer(T&& t) {inner(forward (t));}
            8. int main()
            9. {
            10. X a;
            11. outer(a);
            12. outer(X());
            13. inner(forward (X()));
            14. }
            15. //inner(const X&)
            16. //inner(X&&)
            17. //inner(X&&) 代码说明
                测试代码第13行用X类型的左值 a 来测试forward函数,程序输出表明 outer(a) 调用的是 inner(const X&) 版本,从而证明函数模板outer调用forward函数在将参数左值 a 转发给了inner函数时,成功地保留了参数 a 的左值属性。测试代码第14行用X类型的右值 X() 来测试forward函数,程序输出表明 outer(X()) 调用的是 inner(X&&) 版本,从而证明函数模板outer调用fo