首先要分清:

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 (引用折叠以及完美转发)


★ 答案,这下讲明白了!

完美转发(perfect forwarding)、universal reference、引用折叠(collasping)的更多相关文章

  1. Effective Modern C++:05右值引用、移动语义和完美转发

    移动语义使得编译器得以使用成本较低的移动操作,来代替成本较高的复制操作:完美转发使得人们可以撰写接收任意实参的函数模板,并将其转发到目标函数,目标函数会接收到与转发函数所接收到的完全相同的实参.右值引 ...

  2. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part8:Perfect Forwarding(完美转发):解决方案

    本文为第八部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...

  3. C++ 左值与右值 右值引用 引用折叠 => 完美转发

    左值与右值 什么是左值?什么是右值? 在C++里没有明确定义.看了几个版本,有名字的是左值,没名字的是右值.能被&取地址的是左值,不能被&取地址的是右值.而且左值与右值可以发生转换. ...

  4. C++11 左值引用和右值引用与引用折叠和完美转发

    1.左值与右值 最感性的认识. 当然,左值也是可以在右边的. 左值是可以被修改的,右值不能. 当然取地址也是. 生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短. 下面 ...

  5. 第16课 右值引用(3)_std::forward与完美转发

    1. std::forward原型 template <typename T> T&& forward(typename std::remove_reference< ...

  6. [c++11]右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  7. [转][c++11]我理解的右值引用、移动语义和完美转发

    c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...

  8. C++11--右值引用(Perfect Forwarding)

    /* * 右值引用 2: Perfect Forwarding */ void foo( boVector arg ); // boVector既有移动构造又有拷贝构造 template< ty ...

  9. [C++中级进阶]001_C++0x里的完美转发到底是神马?

    [C++中级进阶]001_C++0x里的完美转发到底是神马? 转载至:http://www.cnblogs.com/alephsoul-alephsoul/archive/2013/01/10/285 ...

随机推荐

  1. pip安装模块使用国内镜像源加速安装

    今天在安装Python模块matplotlib的时候,一直安装不成功,老是提示“socket.timeout: The read operation timed out”或者“Read timed o ...

  2. Jenkins + pipeline + Git + PHP (九)

    一.准备环境介绍 192.168.5.71 # gitlab 仓库IP 192.168.5.72 # 开发环境,用于提交代码等 192.168.5.150 # www.leon.com 运行wordp ...

  3. Ubuntu下搭建Kubernetes集群(1)--安装docker

    可以使用物理机,也可以使用虚拟机. 首先参考https://docs.docker.com/install/linux/docker-ce/ubuntu/ 官方文档学会安装docker. 1.首先移除 ...

  4. 设备树的规范(dts)

    设备树的官方文档: https://www.devicetree.org/specifications/ 一.理论部分 如何看下面这张图: 1)从根节点开始看起,即 / 2)在根节点中有属性以及设备节 ...

  5. E07【餐厅】What would you recommend?

    核心句型 What would you recommend? 你有什么推荐吗? 场景对话: A:What do  you  want  to  eat? 你想吃点什么? B:I don't know. ...

  6. NOIP 2004 合唱队形

    洛谷 P1091 合唱队形 https://www.luogu.org/problemnew/show/P1091 JDOJ 1271: [NOIP2004]合唱队形 T3 https://neooj ...

  7. 使用mytop监控mysql

    mytop 是一个不错的实时查看mysql 状态的命令行工具,使用简单 安装 yum install -y mytop 环境准备 docker-compose 创建服务 version: " ...

  8. c++基础第一篇

    前言:我是从c和c++对比的角度来讲解c++的基础知识. (1)c++格式如下: #include <iostream> //标准输入输出头文件 using namespace std; ...

  9. Comet OJ - Contest #7 C 临时翻出来的题(容斥+状压)

    题意 https://www.cometoj.com/contest/52/problem/C?problem_id=2416 思路 这里提供一种容斥的写法(?好像网上没看到这种写法) 题目要求编号为 ...

  10. AntDeploy发布前端项目到IIS(脱离vs单独使用)

    AntDeploy AntDeploy是一款开源的一键发布部署工具,目的是代替重复性的发布动作,提高部署效率 1.一键部署iis 2.一键部署windows服务 3.一键部署到Docker 4.支持增 ...