zoukankan      html  css  js  c++  java
  • 完美转发(perfect forwarding)、universal reference、引用折叠(collasping)

    首先要分清:

    C++里的值只有两种值:左值、右值。—— 其本质应该是内存中存储的值/instance分两种:一种是持久的,一种是“短暂的”

    只有两种引用: 左值引用、右值引用。 ——引用,就是这个内存地址的助记符(别名)。

    1.  左值引用 需绑定、也只能绑定 左值。 

      同理,右值引用 需绑定、也只能绑定 右值

    2.  有两种特别的引用既可以绑定左值、又可以绑定右值。分别是:

      ①const T&   

      ② universal reference : “T&&  t在发生自动类型推断的时候,它是未定的引用类型”—— 指的是template的推断/ auto推断

     3.  int&&  t = getTemp();     则“getTemp()”是右值

      但用t绑定该右值后,该右值的生命周期就被延长(和t的生命周期一样)。

      注意,此时t已经是持久的值,而且可以对其取地址的。(赋值也没编译报错~)

      ★ 此时: ① t是右值引用   ② t是一个左值  “绑定了右值的右值引用本身 是一个左值”   【这符合 “ 左值持久、右值短暂”】


    再看函数调用:  传参的实质是把实参传递给形参。 其实质的过程却可以细分一下!

    实参 ——>  形参  =  return # ——> “ret”   

    R   res ( in ) ;

    第1种: 值 ——>  引用是引用去绑定值的过程)

     【左值—> 左值引用】  int i = 1; foo(int& m);  ——>   foo(i); 则“传参” 是用形参的引用 绑定实参这个值。

     【右值—> 右值引用】    T GetTemp();  foo(T&& m);  ——>  foo( GetTemp() );    (此时在函数内 可以 move(m) 后 接管这个右值的临时资源了)

    第2种:  值 ——> 值    是给我提供个对象,我根据它再构造一个的过程(拷贝/移动)

     【左值】   int t = 1;    foo(int p);  ——>   foo(t); 则“传参”是用 t 拷贝值给 p。  (值拷贝—— 前提同下。可通过move(lvalue) 将左值转换为右值,以进行移动

      【右值】  T GetTemp();  foo(T  m);  ——>  foo( GetTemp() );         (值移动)   ——前提: 该类型已定义了 const T&的拷贝构造  、 T&&的移动构造

        

    第3种:  引用 ——>   值    值 是给我提供个对象,我根据它再构造一个的过程(拷贝/移动)

     【左值(引用) 】 int i =1;  int& t = i;  foo(int m);   ——>   foo(t);    ★ 对于左值来说,使用左值、左值引用 在传递时 没有区别。都是指 那个持久的值。  相当于第2种“左值——>左值” (值拷贝)

     【右值引用(左值】 T&&  t = GetTemp();   foo(T m);  ——>  foo(t);      相当于第2种“左值——>左值” :  m是临时对象“GetTemp()”的拷贝  (值拷贝)

    第3种: 引用 ——> 引用(是引用去绑定值的过程) +  不存在引用的引用。

     【左值(引用) —> 左值引用】 int i =1;  int& t = i;  foo(int& m);   ——>   foo(t); 则“传参”就相当于  int& ref2 = ref1;

     【右值引用(左值——>  左值引用/ const 左值引用 / universal reference】    T&&  t = GetTemp();   foo(T&& m);    ——>   foo(t)法匹配,“右值引用m 无法绑定 左值t”

                                            T&&  t = GetTemp();   foo(T& m);  ——>  foo( t);   // 引用到了一个右值,但函数会以为自己引用的是左值


    函数返回时  T GetTemp() { return T();}   会发生  T() 这个右值到 “GetTemp()” 这个临时变量的移动。

    那么, 函数调用传参时, 会发生实参到形参的移动吗 ???

        肯定会,比如unique_ptr, iostream 这种只能移动的东西。  

     当然可以:和返回时一样,去 右值去适配 T的构造就行:  GetTemp() ——> T      ,  即    foo(T  t) ;   调用 foo(GetTemp())


     引用折叠

    对一个类型,你可以使用该类型的两种引用: 左值引用 int& 、右值引用 int&&

      你② 不能直接定义 “引用的引用”:   int& &&  i;    int&& & i; 这些写法都是报错的。

    但是,你可以把引用定义为一个新的类型

        typedef  int&  iR;      // iR 就是 左值引用int&

        typedef  int&& iRR;   // iRR 就是右值引用 int&&

    iR, iRR作为新的类型,可以定义它们的两种引用: iR& ,  iR&&   以及   iRR& , iRR &&

      这样仿佛出现了“引用类型 的引用”。

    这时候,就需要“引用折叠”。 —————— 所以,这是C++为了实现 ①+②  而规定的规则。(我觉得的)

    规则就是:凡是右左值引用参与的情况下,最终的类型都会变为左值引用,只有全部为右值引用的情况下才会变为右值引用。

    =============================================================================

    完美转发

    C++11引入了完美转发:在函数模板中,完全依照模板的参数的类型(即保持参数的左值、右值特征),将参数传递给函数模板中调用的另外一个函数。

    template<typename T>

    void foo(T&& t );

    一:用左值去调:

      int i = 1;  foo(i);                 此时 模板推断其类型参数 T 为 int&  ——> 故 T&&  为 int&+ &&  , 折叠为 int&   ——>  所以生成的函数实例为  foo(int& t);     t 是左值。

    二:用右值去调:

      foo(3);                          此时 模板推断其类型参数 T 为 int&&   ——> 故 T&&  为 int&& + &&  , 折叠为 int&&   ——>  所以生成的函数实例为  foo(int&& t);     t 是右值。

    p.s.  模板的参数类型推导规则 ,请另学。

    参考:C++11 reference collapsing and perfect forward (引用折叠以及完美转发)


    ★ 答案,这下讲明白了!

  • 相关阅读:
    ini_set /ini_get函数功能-----PHP
    【转】那个什么都懂的家伙
    word 2007为不同页插入不同页眉页脚
    August 26th 2017 Week 34th Saturday
    【2017-11-08】Linux与openCV:opencv版本查看及库文件位置等
    August 25th 2017 Week 34th Friday
    August 24th 2017 Week 34th Thursday
    August 23rd 2017 Week 34th Wednesday
    August 22nd 2017 Week 34th Tuesday
    August 21st 2017 Week 34th Monday
  • 原文地址:https://www.cnblogs.com/nanlan2017/p/9255319.html
Copyright © 2011-2022 走看看