完美转发(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 (引用折叠以及完美转发)
★ 答案,这下讲明白了!
完美转发(perfect forwarding)、universal reference、引用折叠(collasping)的更多相关文章
- Effective Modern C++:05右值引用、移动语义和完美转发
移动语义使得编译器得以使用成本较低的移动操作,来代替成本较高的复制操作:完美转发使得人们可以撰写接收任意实参的函数模板,并将其转发到目标函数,目标函数会接收到与转发函数所接收到的完全相同的实参.右值引 ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part8:Perfect Forwarding(完美转发):解决方案
本文为第八部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- C++ 左值与右值 右值引用 引用折叠 => 完美转发
左值与右值 什么是左值?什么是右值? 在C++里没有明确定义.看了几个版本,有名字的是左值,没名字的是右值.能被&取地址的是左值,不能被&取地址的是右值.而且左值与右值可以发生转换. ...
- C++11 左值引用和右值引用与引用折叠和完美转发
1.左值与右值 最感性的认识. 当然,左值也是可以在右边的. 左值是可以被修改的,右值不能. 当然取地址也是. 生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短. 下面 ...
- 第16课 右值引用(3)_std::forward与完美转发
1. std::forward原型 template <typename T> T&& forward(typename std::remove_reference< ...
- [c++11]右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
- [转][c++11]我理解的右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
- C++11--右值引用(Perfect Forwarding)
/* * 右值引用 2: Perfect Forwarding */ void foo( boVector arg ); // boVector既有移动构造又有拷贝构造 template< ty ...
- [C++中级进阶]001_C++0x里的完美转发到底是神马?
[C++中级进阶]001_C++0x里的完美转发到底是神马? 转载至:http://www.cnblogs.com/alephsoul-alephsoul/archive/2013/01/10/285 ...
随机推荐
- Linux 信号量之Posix有名字的信号量
信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能. 信号量有三种: Posix有名字的信号量 Posix基于内存的信号量 System V信号量 信号量比互斥锁高级,互斥锁 ...
- 004-OpenStack-计算服务
OpenStack-计算服务 [基于此文章的环境]点我快速打开文章 1.控制节点(controller) 1.1 创库授权 nova_api, nova, 和 nova_cell0 mysql CR ...
- Rust中的Slices
这个slice切片,python中有,go中有, 但确实,Rust中最严格. 精彩见如下URL: Rust 程序设计语言(第二版) 简体中文版 · GitBook (Legacy) https://k ...
- 实现 Trie (前缀树)
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作. 示例: Trie trie = new Trie(); trie.insert(" ...
- Pycharm中使用from appium import webdriver时报错:ModuleNotFoundError: No module named 'appium'
此时先检查一下有没有安装Appium-Python-Client,如果没有安装Appium-Python-Client就在控制台输入pip install Appium-Python-Client进行 ...
- batch、epoch、iteration
深度学习的优化算法,说白了就是梯度下降.每次的参数更新有两种方式. 第一种,遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度.这种方法每更新一次参数都要把数据集里的所有样本都看一遍, ...
- 20191003 「HZOJ NOIP2019 Round #8」20191003模拟
综述 试题为常州集训2019SCDay2 得分\(100+30(0)+28\) 时之终结 问题描述 HZOJ1310 题解 构造题. 发现部分分有一档是 \(Y\) 是 \(2^x\) ,于是自然想到 ...
- 高效Redis工具类
一.引言 本篇博客以redis缓存为主.至于什么是redis缓存?还有没有其它的缓存?哪个缓存的性能会更好?这里就不一一做介绍了!(有兴趣的可以自己去百度一下) 在日常的开发中,我们或多或少(必须)的 ...
- ISerializable接口
继承ISerializable接口可以灵活控制序列化过程 格式化器的工作流程:格式化器再序列化一个对象的时候,发现对象继承了ISerializable接口,那它就会忽略掉类型所有的序列化特性,转而调用 ...
- NET日记
NET日记 NET——day01:https://www.cnblogs.com/noonjuan/diary/2019/07/29/11265942.html NET——day02:https:// ...