item 24: 区分右值引用和universal引用

本文翻译自《effective modern C++》,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢!

博客已经迁移到这里啦

古人曾说事情的真相会让你觉得很自在,但是在适当的情况下,一个良好的谎言同样能解放你。这个Item就是这样一个谎言。但是,因为我们在和软件打交道,所以让我们避开“谎言”这个词,换句话来说:本Item是由“抽象”组成的。

为了声明一个指向T类型的右值引用,你会写T&&。因此我们可以“合理”地假设:如果你在源代码中看到“T&&”,你就看到了一个右值引用。可惜地是,它没有这么简单:

void f(Widget&& param);         // 右值引用Widget&& var1 = Widget();       // 右值引用auto&& var2 = var1;             // 不是右值引用template<typename T>
void f(std::vector<T>&& param); // 右值引用template<typename T>
void f(T&& param);              // 不是右值引用

事实上,“T&&”有两个不同的意思。当然,其中一个是右值引用。这样引用行为就是你所期望的:它们只绑定到右值上去,并且它们的主要职责就是去明确一个对象是可以被move的。

“T&&”的另外一个意思不是左值引用也不是右值引用。这样的引用看起来像是在源文件中的右值引用(也就是,“T&&”),但是它能表现得像是一个左值引用(也就是“T&”)一样。它这样的两重意义让它能绑定到左值(就像左值引用)上去,也能绑定到右值(就像右值引用)上去。另外,它能绑定到const或非const对象上去,也能绑定到volatile或非volatile对象上去,甚至能绑定到const加volatile的对象上去。它能绑定到几乎任何东西上去。这样空前灵活的引用理应拥有它们自己的名字,我叫它们universal引用(万能引用)。

universal引用出现在两种上下文中。最通用的情况是在函数模板参数中,就像来自于上面示例代码的这个例子一样:

template<typename T>
void f(T&& param);              // param是一个universal引用

第二个情况是auto声明,包括上面示例代码中的这一行代码:

auto&& var2 = var1;             // var2是一个universal引用

这两个情况的共同点就是它们都存在类型推导。在模板f中,param的类型正在被推导,并且在var2的声明式中,var2的类型正在被推导。把它们和下面的例子(它们不存在类型推导,同样来自上面的示例代码)比较一下,可以发现,如果你看到不存在类型推导的“T&&”时,你能把它视为右值引用:

void f(Widget&& param);         // 没有类型推导// param是右值引用Widget&& var1 = Widget();       // 没有类型推导// param是右值引用

因为universal引用是引用,它们必须被初始化。universal引用的初始化决定了它代表一个右值还是一个左值。如果初始化为一个右值,universal引用对应右值引用。如果初始化为一个左值,universal引用对应一个左值引用。对于那些属于函数参数的universal引用,它在调用的地方被初始化:

template<typename T>
void f(T&& param);              // param是一个universal引用Widget w;
f(w);                           // 左值被传给f,param的类型是// Widget&(也就是一个左值引用)f(std::move(w));                // 右值被传给f,param的类型是// Widget&&(也就是一个右值引用)

要让一个引用成为universal引用,类型推导是其必要不补充条件。引用声明的格式必须同时正确才行,而且格式很严格。它必须正好是“T&&”。再看一次这个我们之前在示例代码中看过的例子:

template<typename T>
void f(std::vector<T>&& param); // param是一个右值引用

当f被调用时,类型T将被推导(除非调用者显式地指定它,这种边缘情况我们不关心)。但是param类型推导的格式不是“T&&”,而是“std::vector&&”。按照上面的规则,排除了param成为一个universal引用的可能性。因此param是一个右值引用,有时候你的编译器会很高兴地为你确认你是否传入了一个左值给f:

std::vector<int> v;
f(v);                           // 错误!不能绑定一个左值到右值// 引用上去

甚至一个简单的const属性的出场就足以取消引用成为universal的资格:

template<typename T>
void f(const T&& param);        // param是一个右值引用

如果你在一个模板中,并且你看到一个“T&&”类型的函数参数,你可能觉得你能假设它是一个universal引用。但是你不能,因为在模板中不能保证类型推导的存在。考虑一下std::vector中的这个push_back成员函数:

template<class T, class Allocator = allocator<T>>       //来自c++标准库
class vector {
public:void push_back(T&& x);...
};

push_back的参数完全符合universal引用的格式,但是在这个情况中没有类型推导发生。因为push_back不能存在于vector的特定实例之外,并且实例的类型就完全能决定push_back的声明类型了。也就是说

    std::vector<Widget> v;

使得std::vector模板被实例化为下面这样:

class vector<Widget, allocator<Widget>> {
public:void push_back(Widget&& x);     //右值引用...
};

现在你能清楚地发现push_back没有用到类型推导。vector的这个push_back(vector中有两个push_back函数)总是声明一个类型是rvalue-reference-to-T(指向T的右值引用)的参数。

不同的是,std::vector中和push_back概念上相似的emplace_back成员函数用到了类型推导:

template<class T, class Allocator = allocator<T>>
class vector {
public:template <class... Args>void emplace_back(Args&&... args);...
};

在这里,类型参数Args独立于vector的类型参数T,所以每次emplace_back被调用的时候,Args必须被推导。(好吧,Args事实上是一个参数包,不是一个类型参数,但是为了讨论的目的,我们能把它视为一个类型参数。)

事实上emplace_back的类型参数被命名为Args(不是T),但是它仍然是一个universal引用,之前我说universal引用的格式必须是“T&&”。在这里重申一下,我没要求你必须使用名字T。举个例子。下面的模板使用一个universal引用,因为格式(“type&&”)是正确的,并且param的类型将被推导(再说一次,除了调用者显式指定类型的边缘情况):

    template<typename MyTemplateType>       // param是一个void someFunc(MyTemplateType&& param);  // universal引用

我之前说过auto变量也能是universal引用。更加精确一些,用auto&&的格式被推导的变量是universal引用,因为类型推导有发生,并且它有正确的格式(“T&&”)。auto universal引用不像用于函数模板参数的universal引用那么常见,但是他们有时候会在C++11中突然出现。他们在C++14中出现的频率更高,因为C++14的lambda表达式可以声明auto&&参数。举个例子,如果你想要写一个C++14的lambda来记录任意函数调用花费的时间,你能这么做:

auto timeFuncInvocation =[](auto&& func, auto&&... param){start timer;std::forward<decltype(func)>(func){             // 用paramsstd::forward<decltype(params)>(params)...   // 调用func};停止timer并记录逝去的时间。};

如果你对lambda中“std::forward<decltype(blah blah blah)>”(译注:blah blah blah发音为:布拉布拉布拉)的代码感到困惑,这可能只是意味着你还没读过Item 33.不要担心这件事。在本Item中,重要的事情是lambda表达式中声明的auto&&参数。func是一个universal引用,它能被绑定到任何调用的对象上去,不管是左值还是右值。params(译注:原文为args,应该是笔误)是0个或多个universal引用(也就是一个universal引用包),它能被绑定到任何数量的任意类型的对象上去。结果就是,由于auto universal引用的存在,timeFuncInvocation能给绝大多数函数的执行进行计时。(对于为什么是绝大多数而不是任意,请看Item 30。)

把这件事记在心里:我们这整个Item(universal引用的基础)都是一个谎言...额,一个“抽象”!潜在的事实被称为引用折叠,这个话题会在Item 28中专门讨论。但是事实并不会让抽象失效。区分右值引用和universal引用将帮助你更精确地阅读源代码(“我看到的T&&只能绑定到右值上,还是能绑定到所有东西上呢?”),并且在你和同事讨论的时候,它能让你避免歧义。(“我在这里使用一个universal引用,不是一个右值引用...”)。它也能让你搞懂Item 25和Item 26的意思,这两个Item都依赖于这两个引用的区别。所以,拥抱抽象并陶醉于此吧。就像牛顿的运动定律(学术上来说是错误的)一样,比起爱因斯坦的相对论(“事实”)来说它通常一样好用并且更简单,universal引用的概念也是这样,比起工作在引用折叠的细节来说,它是更好的选择。

            你要记住的事
  • 如果一个函数模板参数有T&&的格式,并且会被推导,或者一个对象使用auto&&来声明,那么参数或对象就是一个universal引用。
  • 如果类型推导的格式不是准确的type&&,或者如果类型推导没有发生,type&&就是一个右值引用。
  • 如果用右值来初始化,universal引用相当于右值引用。如果用左值来初始化,则相当于左值引用。

转载于:https://www.cnblogs.com/boydfd/p/5251797.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257586.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebLogic11g-常用运维操作

转自&#xff1a;https://dead-knight.iteye.com/blog/1940399 希望这篇能把weblogic运维时经常遇到的问题、常用的配置汇总到一起。 1、配置jvm参数&#xff1a; 一般在domain启动过程中会看到以下启动的日志信息&#xff0c;如下图所示&#xff1a; 图中红色方框部分为启动we…

牛腩新闻发布系统(一):SQLHelper重构(一)

导读&#xff1a;在机房重构的时候&#xff0c;就用到了SQLHelper&#xff0c;但那时候即使把代码反复看了很多遍&#xff0c;也看了注释&#xff0c;还和同学交流&#xff0c;也依然是半懂不懂。现在&#xff0c;我再次用到了SQLhelper这个东西&#xff0c;就来说说SQLHelper是…

OPENCV图像轮廓检测

前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像轮廓检测. 一.图像轮廓检测 在opencv中,轮廓对应着一系列的点的集合,open…

mysql 5.7.11 授权_mysql 5.7.11 安装配置教程

六步轻松搞定mysql5.7.11的安装1、下载安装包。mysql-5.7.11版本&#xff1a;2、拷贝到任意盘&#xff1a;例如&#xff0c;解压后拷贝文件夹至C盘&#xff1a;C:\Program Files\mysql。建议文件夹名字使用英文。3、配置环境变量&#xff1a;计算机—>右键—>高级系统设置…

iOS 面试之Block

转自&#xff1a;http://blog.csdn.net/xunyn/article/details/11658261 1 什么是block 对于闭包&#xff08;block),有很多定义&#xff0c;其中闭包就是能够读取其它函数内部变量的函数&#xff0c;这个定义即接近本质又较好理解。对于刚接触Block的同学&#xff0c;会觉得有…

当安全遇到大数据 “永恒之蓝”也将无所遁形!

文章讲的是当安全遇到大数据 “永恒之蓝”也将无所遁形&#xff01;5月12日&#xff0c;席卷全球的勒索病毒“永恒之蓝”让全世界都为之震动&#xff0c;这是迄今为止全球最大规模的勒索病毒网络攻击&#xff0c;100多个国家受到病毒感染&#xff0c;国内中石油、公安内网、高校…

[ES] 安装

1.ElasticSearch安装的准备工作 Linux&#xff1a;CentOS6.4 Elasticsearc:elasticsearch-2.2.0 JDK:jdk-7u79-linux-x64 IK:1.8.0 MAVEN:apache-maven-3.3.3-bin 2.配置网络静态文件 虚拟机设置桥接模式 配置&#xff1a;vim /etc/sysconfig/network-scripts/ifcfg-eth0 DEVIC…

语言基础之description方法

1.description方法的一般用处 1: // 指针变量的地址 2: NSLog("%p", &p); 3: // 对象的地址 4: NSLog("%p", p); 5: // <类名&#xff1a;对象地址> 6: NSLog("%", p); 1: Class c [Person class]; 2: …

亚信安全协助绿谷制药确保“秘方”安全

近几年&#xff0c;我国医药生物技术发展态势迅猛&#xff0c;加强知识产权保护己成为当务之急。为确保制药配方数据和生产管理信息系统安全&#xff0c;上海绿谷制药有限公司采用亚信安全服务器深度安全防护系统&#xff08;Deep Security&#xff09;和亚信安全防毒墙网络版&…

mysql判断叠字_格律诗的八大语法特点

古风的语法&#xff0c;本来就和散文的语法大致相同&#xff0c;直到近体诗&#xff0c;才渐和散文不同&#xff0c;原因是&#xff0c;首先在区区五字或七字之中&#xff0c;要施展丰富的想象&#xff0c;不能不力求简洁&#xff0c;凡可省去而不至于影响语意的字&#xff0c;…

旅游行业春节档期的大数据营销

本文讲的是旅游行业春节档期的大数据营销,虽然我国是以传统农耕文化为主导的社会&#xff0c;每逢春节讲究返乡团聚。但现代化的城市文明更是对很多人的生活方式产生了影响&#xff0c;特别是生活在大城市中的年轻人&#xff0c;以及由年轻人构成的小家庭来说&#xff0c;春节的…

openwrt lamp

https://applefreak111.wordpress.com/2013/03/12/howtoopenwrt-lamp-stack%E5%AE%89%E8%A3%9D/opkg update安裝Lighttpd, MySQL 5, 和PHP 5。opkg install lighttpd lighttpd-mod-cgi lighttpd-mod-fastcgivi /etc/lighttpd/lighttpd.confcgi.assign ( “.php” > “/usr/…

MySQL本天早上8点到明早8点_似乎找到 OSChina 早上 8 点钟容易宕机的原因

最近一段时间&#xff0c;OSChina 网站在早上 8 点出头的时候很容易因为数据库连接池爆满而导致网站宕机。表现的情况是数据库处理大量的查询&#xff0c;堆积大量并发连接&#xff0c;导致无法再连接到数据库&#xff0c;执行一个简单的查询速度也非常慢&#xff0c;数据库机器…

基于Eclipse搭建STM32开源开发环境

最近项目不忙&#xff0c;想着没事看看简单的嵌入式&#xff0c;弄弄物联网什么的。于是就从廉价的STM32开刀了。因为一直是做PC软件开发的&#xff0c;那VS的智能感知那叫一个爽啊&#xff0c;相比之下&#xff0c;觉得这个Keil简直就像文本编辑器一样low。于是想换一个开发环…

数据中心不再有空调、风扇等冷却装置会怎样?

数据中心的变革有望依赖移动设备实现&#xff0c;手机里轻便设备或将成为下一代数据中心的基础设施&#xff0c;服务Google和Facebook等大型的应用程序服务企业。同时&#xff0c;这种商业模式也会构建新一代企业的发展形态&#xff0c;为初创企业带来前所未有的机遇。 CSDN大数…

.NET 数据库缓存依赖策略实现

处理大型门户网站 一般都需要 使用缓存技术这个web加速器在 PHP 和 java 一般 使用的是 基于squid 来做. 当然在 windows .NET 平台也是可以的 squid有 windows版本.这个以后再去研究,现在 就介绍一下 .NET 自带的 缓存策略.Microsoft的petshop就用到了它;  一、基于数据库触…

大数据面临的挑战:当大数据遭遇云计算

本文讲的是大数据面临的挑战&#xff1a;当大数据遭遇云计算,大数据正在彻底改变IT世界。那么&#xff0c;什么样的数据谈得上数据呢? 根据IDC的报告&#xff0c;未来十年全球大数据将增加50倍。仅在2011年&#xff0c;我们就将看到1.8ZB(也就是1.8万亿GB)的大数据创建产生。这…

Climbing Stairs - Print Path

stair climbing&#xff0c; print out all of possible solutions of the methods to climb a stars, you are allowed climb one or two steps for each time; what is time/space complexity? &#xff08;use recursion&#xff09; 这道题难是难在这个ArrayList<Strin…

java 单例设计_Java 之单例设计模式

设计模式: 对问题行之有效的解决方式, 其实它是一种思想.单例设计模式解决的问题:就是可以保证一个类在内存中的对象唯一性. 即单个实例.比如对于A 和 B 两个程序使用同一个配置信息对象时, A 对配置信息作出修改, B 也与之对应的更新配置信息, 即需要保证该对象的唯一性.如何保…

Javascript之RegExp

RegExp对象的构造器 new RegExp(pattern[, flags]) pattern 正则表达式文本flags 该参数可以是下面几个值的任意组合&#xff1a;g 全局匹配i 忽略大小写m 让开始和结束字符&#xff08;^ 和 $&#xff09;工作在多行模式&#xff08;也就是&#xff0c;^ 和 $ 可以匹配字符串中…