zoukankan      html  css  js  c++  java
  • std::move和std::forward

      std::move和std::forward是C++11中新增的标准库函数,分别用于实现移动语义和完美转发
      下面让我们分析一下这两个函数在gcc4.6中的具体实现。

    预备知识

    引用折叠规则

    T& + & => T&
    T&& + & => T&
    T& + && => T&
    T&& + && => T&&

    就是左值引用会传染,沾上一个左值引用就变左值引用了,只有都为纯右值引用结果才为右值引用&& && = &&。

    模板参数推导规则

     只有调用前fpar是&&,apar是&时,调用后T的实际类型才是A&,其余3种情况下都是A。

    1、当模板参数为T时

      因为是值传递,所以expr的所有修饰特性都会被忽略,const, 引用,volatile等,都被忽略。
    • 如果表达式不是指针,T和x的类型都是非引用类型,cv限定符(const、volatile)去除。比如表达式是int、int&、int&&、const int、const int&,只要不是指针,T和x最终都是int;
    • 如果是指针,T和x都是对应的指针类型,cv限定符保留;注意指针常量的情况,由于指针常量本质上是一个常量,所以cv限定符去除。
     1 #include <iostream>
     2 
     3 template<typename T>
     4 void f(T param) {
     5     T a;
     6     std::cout << param;
     7 }// param 为值传递
     8 // 因为是值传递,所以expr的所有修饰特性都会被忽略,const, 引用,volatile等,都被忽略
     9 int main()
    10 {
    11     int x = 2;
    12     const int cx = x;
    13     int& rx = x;
    14     const int& crx = x;
    15     int* p = &x;
    16     const int* cip = &x;
    17     int* const icp = &x;
    18     f(x);     // x: int                param : int,         T : int
    19     f(cx);    // cx: const int,        param : int,         T : int
    20     f(rx);    // rx : int&,            param : int,         T : int 
    21     f(crx);   // crx : const int&,     param : int,         T : int 
    22     f(p);     // p : int*,             param : int*,        T : int*
    23     f(cip);   // cip : const int*,     param : const int*,  T : const int*
    24     f(icp);   // icp : int* const,     param : int*,        T : int*
    25 
    26     return 0;
    27 }

    2、当模板参数为T&时

    • 对于T&,不管表达式是什么,parma一定是左值引用类型(表达式是指针则x是指针的引用),cv限定符保留;
    • 对于T*,和T&类似,parma一定是指针类型,cv限定符保留;
     1 template<typename T>
     2 void f(T& param)
     3 {
     4     T a(0);
     5 }
     6 
     7 template<typename T>
     8 void pf(T* param)
     9 {
    10     T a(0);
    11 }
    12 
    13 template<typename T>
    14 void cf(const T& param)
    15 {
    16     T a(0);
    17 }
    18 
    19 
    20 int main()
    21 {
    22     int x = 2;
    23     const int cx = x;
    24     int& rx = x;
    25     const int& crx = x;
    26     int* p = &x;
    27     const int* cip = &x;
    28     int* const icp = &x;
    29     int&& rrx = 2;
    30 
    31     f(x);     // x: int                param : int&,            T : int
    32     f(cx);    // cx: const int,        param : const int&,      T : const int
    33     f(rx);    // rx : int&,            param : int&,            T : int 
    34     f(crx);   // crx : const int&,     param : const int&,      T : const int 
    35     f(p);     // p : int*,             param : int* &,            T : int*
    36     f(cip);   // cip : const int*,     param : const int* &,    T : const int*
    37     f(icp);   // icp : int* const,     param : int* const &,    T : int* const
    38     f(rrx);   // rrx : int&&,           param : int&,            T : int
    39 
    40 
    41 
    42 
    43     pf(p);     // p : int*,             param : int*,        T : int
    44     pf(cip);   // cip : const int*,     param : const int*,  T : const int
    45     pf(icp);   // icp : int* const,     param : int*,        T : int
    46 
    47 
    48 
    49     cf(x);     // x: int                param : const int&,         T : int
    50     cf(cx);    // cx: const int,        param : const int&,         T : int
    51     cf(rx);    // rx : int&,            param : const int&,         T : int 
    52     cf(crx);   // crx : const int&,     param : const int&,         T : int 
    53     cf(p);     // p : int*,             param : int* const&,        T : int*
    54     cf(cip);   // cip : const int*,     param : const int* const&,  T : const int*
    55     cf(icp);   // icp : int* const,     param : int* const &,        T : int*
    56     cf(rrx);   // rrx : int&&,            param : const int&,            T : int
    57    
    58 
    59     return 0;
    60 }

    3、当模板参数为T&&时

    • 不管表达式是什么,parma都是引用类型:
    • 如果表达式是左值,则parma是左值引用,cv限定符保留,即采用和T&相同的规则,只不过因为T&&多了个&,为了转为左值引用需要引入引用折叠的概念。通过下面的例子可以发现,T一定是左值引用类型,这是T被推断为引用类型的唯一情况
    • 如果表达式是右值,则x是右值引用类型;
     1 template<typename T>
     2 void f(T&& x);
     3 
     4 int i;
     5 int& r;
     6 int&& rr;
     7 const int& cr;
     8 int* p;
     9 const int* const q;
    10 // 都是左值,采用的规则和T&的相同
    11 f(i); // x是int& => T&&是int&,int& + && = int& => T是int&
    12 f(r); // x是int& => T&&是int&,int& + && = int& => T是int&
    13 f(rr); // x是int& => T&&是int&,int& + && = int& => T是int&
    14 f(cr); // x是const int& => T&&是const int& => T是const int&
    15 f(p); // x是int* & => T&&是int* & => T是int* &
    16 f(q); // x是const int* const & => T&&是const int* const & => T是const int* const &
    17 
    18 // 表达式是右值
    19 f(42); // x是int&& => T&&是int&& => T是int

    std::remove_reference

    std::remove_reference为C++11标准库中的元函数,其功能为去除类型中的引用。
    std::remove_reference<U&>::type ≡ U
    std::remove_reference<U&&>::type ≡ U
    std::remove_reference<U>::type ≡ U

    static_cast<T&&>(t)

    以下语法形式将把表达式 t 转换为T类型的右值(准确的说是无名右值引用,是右值的一种)
    static_cast<T&&>(t)

    无名的右值引用是右值
    具名的右值引用是左值。


    注:本文中 ≡ 含义为“即,等价于“。

    std::move

    函数功能

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

    源码与测试代码

    1   template<typename _Tp>
    2     inline typename std::remove_reference<_Tp>::type&&
    3     move(_Tp&& __t)
    4     { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
     1 #include<iostream>
     2 using namespace std;
     3  
     4 struct X {};
     5  
     6 int main()
     7 {
     8     X a;
     9     X&& b = move(a);
    10     X&& c = move(X());
    11 }

    std::forward

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

    源码与测试代码

     1   /// forward (as per N3143)
     2   template<typename _Tp>
     3     inline _Tp&&
     4     forward(typename std::remove_reference<_Tp>::type& __t) 
     5     { return static_cast<_Tp&&>(__t); }
     6  
     7   template<typename _Tp>
     8     inline _Tp&&
     9     forward(typename std::remove_reference<_Tp>::type&& __t) 
    10     {
    11       static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
    12             " substituting _Tp is an lvalue reference type");
    13       return static_cast<_Tp&&>(__t);
    14     }
     1 #include<iostream>
     2 using namespace std;
     3  
     4 struct X {};
     5 void inner(const X&) {cout << "inner(const X&)" << endl;}
     6 void inner(X&&) {cout << "inner(X&&)" << endl;}
     7 template<typename T>
     8 void outer(T&& t) {inner(forward<T>(t));}
     9  
    10 int main()
    11 {
    12     X a;
    13     outer(a);
    14     outer(X());
    15     inner(forward<X>(X()));
    16 }
    17 //inner(const X&)
    18 //inner(X&&)
    19 //inner(X&&)

    参考 

    zwvistaC++11尝鲜:std::move和std::forward源码分析

    引用叠加规则和模板参数类型推导规则

  • 相关阅读:
    [转贴] 2016一月12日起.NET 4, 4.5 and 4.5.1 停止安全更新、技术支持 or hotfix
    Windows Azure 入门 -- VS 2015部署 ASP.NET网站(项目) 与 数据库
    [职场]工作多久才能换工作?下一个工作年薪该多高?
    [转贴] ASP.NET -- Web Service (.asmx) & JSON
    ADO.NET #3-1 (GridView + DataReader + SqlCommand)完全手写Code Behind
    File 与 Log #3--动态加入控件,[图片版]访客计数器(用.txt档案来记录)
    小图示优化
    GridView的 PreRender事件与范例--GridView + CheckBox,点选多列资料(复选删除)
    [习题] FindControl 简单练习--GridView + CheckBox,点选多列数据(复选删除)#3 List或数组
    GridView的 PreRender事件与 RowCreated、RowDataBound事件大乱斗
  • 原文地址:https://www.cnblogs.com/chen-cs/p/13235207.html
Copyright © 2011-2022 走看看