《Effective Modern C++》翻译--条款4:了解怎样查看推导出的类型

条款4:了解怎样查看推导出的类型

那些想要了解编译器怎样推导出的类型的人通常分为两个阵营。

第一种阵营是实用主义者。他们的动力通常来自于编敲代码过程中(比如他们还在调试解决中),他们利用编译器进行寻找,并相信这个能帮他们找到问题的根源。另外一种是经验主义者。他们正在探索条款1-3所描写叙述的推导规则。

而且从大量的推导情景中确认他们预測的结果(“对于这段代码,我觉得推导出的类型将会是…”),可是有时候。他们仅仅是想简单的回答假设这样,会怎么样呢之类的问题?他们可能想知道假设我用一个universal reference(见条款26)替代一个左值的常量形參(比如在函数的參数列表中用T&&替代const T&)模板类型推导的结果会改变吗?

无论你属于哪一个阵营(二者都是合理的),你所要使用的工具取决于你想要在软件开发的哪一个阶段知道编译器推导出的结果。我们会阐述3种可行的方法:在编辑代码的时获得推导的类型。在编译时获得推导的类型,在执行时获得推导的类型。

IDE编辑器

当你在IDE中的编辑代码时,在你将鼠标悬停在程序实体(比如变量,參数。函数等等)上的时候。编译器显示他们的类型。

比如,在以下的代码中。

const int theAnswer = 42 ;auto x = theAnswer;
auto y = &theAnswer;

IDE编辑器非常可能会显示出x的类型是int,y的类型是const int*。

对于这项工作。你的代码不能过于复杂,由于是IDE内部的C++编译器让IDE提供了这一项信息。

假设编译器不能充分理解并解析你的代码,产生类型推导的结果,它就无法给你显示类型推导的结果。

编译器的诊断

一个有效的得知编译器对某一类型推导出的结果方法是让它产生一个编译期的错误。由于错误的报告信息肯定会提到引起错误的类型。

假如,我们想要知道上一个代码中的x和y被推导出的类型。我们首先声明一个类模板,可是不定义它。代码会像以下这样:

template<typename T>                   //declaration only for TD
class TD;                              //TD == "Type Displayer"

试图实例化这个模板会产生一个错误信息,由于没有模板的定义和实例。为了要查看x和y的类型,仅仅须要用它们的类型实例化TD:

TD<decltype(x)> xType                 //elicit errors containing
TD<decltype(y)> yType                 //x's and y's types;//see Item 3 for decltype info

我使用这样的形式的变量名:variableNameType。由于:它们趋向于产生足够实用的错误信息。对于上面的代码,当中一个编译器的错误诊断信息例如以下所看到的(我高亮了我们想要的类型推导结果)

error: aggregate 'TD<int> xType' has incomplete type and 
cannot be defined 
error: aggregate 'TD<const int *>yType' has incomplete type 
and cannot be defined

还有一个编译器提供了一样的信息,可是格式有所不同:

error: 'xType' uses undefined class 'TD<int>' 
error: 'yType' uses undefined class 'TD<const int *>'

把格式上的不同放到一旁,我所測试的全部编译器都提供了包含实用的类型错误诊断信息。

执行期间的输出

利用printf方法(并非说我推荐你使用printf)显示类型的信息不能在程序执行时期使用。可是它须要对输出格式的全然控制。

难点是怎样让变量的类型能以文本的方式合理的表现出来。你可能会觉得“没有问题”typeid和std::type_info::name会解决问题的。

你觉得我们能够写下以下的代码来知道x和y 的类型:

std::cout << typeid(x).name() << '\n';     // display types for
std::cout << typeid(y).name() << '\n';     // x and y

这种方法依赖于typeid作用于一个对象上时,返回类型为std::type_info这一个事实,type_info有一个叫name的成员函数,提供了一个C风格的字符串(比如 const char*)来表示这个类型的名字。

调用std::type_info的name并不保证返回的东西一定是清楚明了的,可是会尽可能的提供帮助。

不同的编译器提供的程度各有不同,比如:GNU和Clang编译器将x的类型表示为”i”,将y的类型表示为”PKI”,一旦你了解i意味着int,pk意味着pointer to Konst const(两个编译器都提供一个C++ filt工具,来对这些重整后的名字进行解码)。理解编译器的输出将变得easy起来,Microsoft的编译器提供了更清楚的输出,x的类型是int,y的类型是int const*.

由于对x和y显示的结果是正确的,你可能会觉得问题已经攻克了。可是我们不能草率。想想以下这个更复杂的样例:

template<typename T>             // template function to
void f(const T& param);          // be called 
std::vector<Widget> createVec(); // factory function 
const auto vw = createVec();     // init vw w/factory return
if (!vw.empty()) { 
f(&vw[0]);                       // call f
}

当你想知道编译器推导出的类型是什么的时候。这段代码更具有代表性,由于它牵涉到了一个用户自己定义类型widget,一个std容器std::vector。一个auto变量,比如。你可能想知道模板參数T的类型。和函数參数f的类型。

使用typeid看起来是非常直接的方法。仅仅是在f中对你想知道的类型加上一些代码:

template<typename T> 
void f(const T& param) 
{ using std::cout; cout << "T = " << typeid(T).name() << '\n';         // show T cout << "param = " << typeid(param).name() << '\n'; // show param's type
...  
}

GNU和Clang的执行结果是以下这样:

T = PK6Widget 
param = PK6Widget

我们已经知道PK意味着pointer to const,而6代表了类的名字中有多少个字母(Widget),所以这两个编译器告诉了我们T和param的类型都是const Widget*

微软的编译器提供了以下的结果

T = class Widget const * 
param = class Widget const *

这三个编译器都提供了一样的信息。这也许暗示了结果应该是准确的。可是让我们看的更仔细一点,在模板f中,param的类型被声明为constT&,既然如此的话,param和T的类型一样难道不让人感到奇怪吗,假设T的类型是int,param的类型应该是const int&,看,一点都不一样。

令人悲哀的是std::type_info::name的结果并非可依赖的。在这个样例中,三个编译器对于param的结果都是不对的。此外。它们必须是错误的。由于标准(specification)规定被std::type_info::name处理的类型是被依照按值传递给模板对待的,像条款1解释的那样。这意味着假设类型本身是一个引用的话,引用部分是被忽略掉的,假设引用去掉之后还含有const,常量性也将被忽略掉,,这就是为什么const Widget* const &的类型被显示为const Widget*,首先类型的引用部分被忽略了,接着结果的常量性也被忽略了。

相同令人伤心的是,IDE提供的类型信息相同也是不可靠的,或者说不是那么的实用,对于这个样例,我所知道的编译器将T的类型显示为(这不是我编造出来的):

const 
std::_Simple_types<std::_Wrap_alloc<std::_Vec_base_types<Widget, 
std::allocator<Widget> >::_Alloc>::value_type>::value_type *

将param的类型显示为:

const std::_Simple_types<...>::value_type *const &

这个显示没有T的那么吓人了。中间的…仅仅是意味着IDE告诉你。我将T的类型显示用…替代了。

我的理解是大多数显示在这里的东西是由于typedef造成的,一旦你通过typedef来获得潜在的类型信息,你会得到你所寻找的。但须要做一些工作来消除IDE最初显示出的一些类型,幸运的话, 你的IDE编辑器会对这样的代码处理的更好。

在我的经验中,使用编译器的错误诊断信息来知道变量被推导出的类型是相对可靠的方法,利用修订之后的函数模板f来实例化仅仅是声明的模板TD。修订之后的f看起来像以下这样

template<typename T> 
void f(const T& param) 
{ TD<T> TType;                   // elicit errors containing TD<decltype(param)> paramType; // T's and param's types … 
}

GNU。Clang和Microsoft的编译器都提供了带有T和param正确类型的错误信息,当时显示的格式各有不同,比如在GUN中(格式经过了一点轻微的改动)

error: 'TD<const Widget *> TType' has incomplete type 
error: 'TD<const Widget * const &> paramType' has incomplete 
type

除了typeid

假设你想要在执行时获得更正确的推导类型是什么,我们已经知道typeid并非一个可靠的方法,一个可行的方法是自己实现一套机制来完毕从一个类型到它的表示的映射,概念上这并不困难。你仅仅须要利用type trait和模板元编程的方法来将一个完整类型拆分开(使用std::is_const,std::is_ponter,std::is_lvalue_reference之类的type trait),你还须要自己完毕类型的每一部分的字符串表示(虽然你依然须要typeid和std::type_info::name来产生用户自己定义格式的字符串表达)。

假设你常常须要使用这种方法,而且觉得花费在调试,文档,维护上的努力是值得的。那么这是一个合理的方法(If you’d use such a facility often enough to justify the effort needed to write, debug,document, and maintain it, that’s a reasonable approach),可是假设你更喜欢那些移植性不是非常强的可是能轻易实现而且提供的结果比typeid更好的代码的。 你须要注意到非常多编译器都提供了语言的扩展来产生一个函数签名的字符串表达,包含从模板中实例化的函数,模板和模板參数的类型。

比如。GNU和Clang都支持PRETTY_FUNCTION,Microsoft支持了FUNCSIG,他们代表了一个变量(在 GNU和Clang中)或是一个宏(在Microsoft中),假设我们将模板f这么实现的话

template<typename T> 
void f(const T& param) 
{#if defined(__GNUC__)                          //For GNU and std::cout << __PRETTY_FUNCTION__ << '\n';  // Clang 
#elif defined(_MSC_VER) std::cout << __FUNCSIG__ << '\n';          //For Microsoft 
#endif 
… 
}

像之前那样调用f,

std::vector<Widget> createVec();  // factory function const auto vw = createVec();      // init vw w/factory return
if (!vw.empty()) { 
f(&vw[0]);                        //call f 
...
}

在GNU中我们得到了以下的结果

void f(const T&) [with T = const Widget*]

告诉我们T的类型被推导为const Widget*(和我们用typeid得到的结果一样,可是前面没有PK的编码和类名前面的6),同一时候它也告诉我们f參数类型是const T&,假设我们依照这个格式扩展T,我们得到f的类型是const Widget * const&,和typeid的答案不同,可是和使用没有定义的模板,产生的错误诊断信息中的类型信息一致。所以它是正确的。

Microsoft的 FUNCSIG提供了以下的输出:

void __cdecl f<const classWidget*>(const class Widget *const &)

尖括号中的类型是T被推导的类型,为const Widget*。

和我们用typeid得到的结果一样。

括号内的类型是函数參数的类型。是const Widget* const&。和我们用typeid得到的结果不一样。但相同和我们使用TD在编译期得到的类型信息一致。

Clang的PRETTY_FUNCTION,虽然使用了和GNU一样的名字,可是格式却和GNU或是Microsoft的不一样:

void f(const Widget *const &)

它直接显示出了參数的类型,可是须要我们自己去推导出T的类型被推导为了const Widget*(或者我们也能够通过typeid来获得T的类型)

IDE编辑器。编译器的错误诊断信息,typeid和PRETTY_FUNCTION,FUNCSIG之类的语言扩展仅仅仅仅是帮助你弄明确编译器推导出的结果是什么。可是最后,没有什么能替代条款1-3中所描写叙述的类型推导相关的推导规则。

请记住:

•能够通过使用IDE编译器、编译错误信息、typeid、PRETTY_FUNCTIONFUNCSIG这样的语言扩展等。查看类型推导。

•一些工具提供的类型推导结果可能既没实用也不准确,所以理解C++类型推导的原则十分必要。

==============================================================
译者凝视:
IDE 即Integrated Development Environment。是“集成开发环境”的英文缩写,能够辅助开发程序的应用软件。

转载于:https://www.cnblogs.com/gavanwanggw/p/7054562.html

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

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

相关文章

julia 数组类型转换_在Julia中确定类型的超类型

julia 数组类型转换To determine the supertype of a type – we use the supertype() function, it accepts a data type and returns the concrete supertype of the given type. 要确定类型的超类型 –我们使用supertype()函数 &#xff0c;该函数接受数据类型并返回给定类型…

【js】JavaScript parser实现浅析

最近笔者的团队迁移了webpack2&#xff0c;在迁移过程中&#xff0c;笔者发现webpack2中有相当多的兼容代码&#xff0c;虽然外界有很多声音一直在质疑作者为什么要破坏性更新&#xff0c;其实大家也都知道webpack1那种过于“灵活”的配置方式是有待商榷的&#xff0c;所以作者…

图形学 射线相交算法_计算机图形学中的阴极射线管(CRT)

图形学 射线相交算法什么是阴极射线管(CRT)&#xff1f; (What is Cathode Ray Tube (CRT)?) CRT stands for "Cathode Ray Tube". CRT代表“ 阴极射线管” 。 Cathode Ray Tube is a technology that is used widely in the traditional televisions and screens.…

mysql8安装目录linux7.5_Linux系统下 MySQL 5.7和8.0 版本安装指南

一. 准备工作1 删除本地CentOS7中的mariadb&#xff1a;查看系统中是否已安装 mariadb 服务&#xff1a;rpm -qa | grep mariadb或yum list installed | grep mariadb如果已安装则删除 mariadb及其依赖的包&#xff1a;yum -y remove mariadb-libs-5.5.44-2.el7.centos.x86_64关…

ruby array_Ruby中带有示例的Array.fill()方法(1)

ruby arrayArray.fill()方法 (Array.fill() Method) In this article, we will study about Array.fill() method. You all must be thinking the method must be doing something related to populate the Array instance. Well, we will figure this out in the rest of our …

python二分法查找程序_Python程序查找最大EVEN数

python二分法查找程序Input N integer numbers and we have to find the maximum even number. 输入N个整数&#xff0c;我们必须找到最大的偶数。 There are many ways of doing this but this time, we have to thought of most computationally efficient algorithm to do …

如何快速精确的和leader沟通

2019独角兽企业重金招聘Python工程师标准>>> 【缘起】 一个同学找我讨论个事情&#xff0c;沟通了一会还是不确定要表达什么&#xff0c;希望我配合什么。结合自己的经验&#xff0c;简单的聊聊“如何快速精准的和leader沟通一件事”。 【员工角度的潜在困惑&#x…

java字符串最长回文串_Java中的字符串回文程序

java字符串最长回文串Given a string and we have to check whether it is palindrome string or not. 给定一个字符串&#xff0c;我们必须检查它是否是回文字符串。 A string that is equal to its reverse string is known as palindrome string. To implement the program…

UOJ#31 【UR #2】猪猪侠再战括号序列

传送门http://uoj.ac/problem/31 大家好我是来自百度贴吧的_叫我猪猪侠&#xff0c;英文名叫_CallMeGGBond。 我不曾上过大学&#xff0c;但这不影响我对离散数学、复杂性分析等领域的兴趣&#xff1b;尤其是括号序列理论&#xff0c;一度令我沉浸其中&#xff0c;无法自拔。至…

li怎么让文字在图片下面_div+css(ul li)实现图片上文字下列表布局

css样式表代码&#xff1a;html布局代码&#xff1a;效果图&#xff1a;html布局部分&#xff0c;可根据自己需要添加对应的div即可。1、CSS关键样式单词解释1)、ul.imglist{ margin:0 auto; width:536px; overflow:hidden}使用margin:0 auto&#xff0c;让ul结构布局居中&…

如何使用React Native样式表?

Without wasting much time, a style sheet as commonly known in a CSS is an object or block of code of many styling properties and values which is applied in a code when called. 在不浪费大量时间的情况下&#xff0c;CSS中通常已知的样式表是具有许多样式属性和值的…

【iCore1S 双核心板_ARM】例程三:EXTI中断输入实验——读取ARM按键状态

实验原理&#xff1a; 按键的一端与STM32的GPIO(PB9)相连&#xff0c;且PB9外接一个1k大小的限流上接电阻。 初始化时把PB9设置成输入模式&#xff0c;当按键弹起时&#xff0c;PB9由于上拉电阻的作用呈高电平&#xff08;3.3V&#xff09;&#xff1b; 当按键按下时&#xff0…

MySQL小黑框怎么打开_打开你的小黑框命令行,来跟我一起嗨嗨嗨

文章更新于2020-03-16关于电脑位数&#xff1a;位数代表cpu可寻址的内存地址大小。32位的cpu最多可使用4GB内存&#xff0c;而64位cpu能处理的内存范围就高多了。操作系统也类似&#xff0c;只要看到操作系统里面能识别8GB内存就可以知道cpu和操作系统都是64位。一、常用的 cmd…

您如何从Python的stdin中读取信息?

Python supports following ways to read an input from stdin (standard input), Python支持以下方式从stdin(标准输入)读取输入 &#xff0c; 1)使用sys.stdin (1) Using sys.stdin) sys.stdin is a file-like object on which we can call functions read() or readlines()…

CentOS7下的AIDE***检测配置

1、AIDE的简单介绍AIDE通过扫描一台&#xff08;未被篡改&#xff09;的Linux服务器的文件系统来构建文件属性数据库&#xff0c;以后将服务器文件属性与数据库中的进行校对&#xff0c;然后在服务器运行时对被修改的索引了的文件发出警告。出于这个原因&#xff0c;AIDE必须在…

mysql主从不同步 tar_Mysql主从不同步问题处理案例

在使用Mysql的主从复制架构中&#xff0c;有两个比较头疼的问题&#xff1a;1、主从数据不同步后如何处理2、主从同步延迟问题如何解决本文将根据实际案例来分析下问题1&#xff0c;至于问题2多数文档介绍的办法是启用多线程复制来解决&#xff0c;言归正传&#xff0c;这里的问…

编程语言优缺点_R编程语言的优缺点

编程语言优缺点In general, the R programming language is considered as the machine learning language. This is widely employed in the applications where the data analysis, visualization, and the sampling process are involved. The R programming language is ta…

mysql重做日志与binlog日志区别_MySQL中的重做日志(redo log),回滚日志(undo log),以及二进制日志(binlog)的简单总结...

MySQL中有六种日志文件&#xff0c;分别是重做日志(redo log)回滚日志(undo log)二进制日志(binlog)错误日志(errorlog)慢查询日志(slow query log)一般查询日志(general log)中继日志(relay log)。其中重做日志和回滚日志与事务操作息息相关&#xff0c;二进制日志也与事务操作…

python 绘制三角函数_Python | 绘制三角函数

python 绘制三角函数Trigonometry is one of the most important parts in engineering and many times, therefore matplotlib.pyplot in combination with NumPy can help us to plot our desired trigonometric functions. In this article, we are going to introduce a fe…

《深入理解Elasticsearch(原书第2版)》一2.3.3 把查询模板保存到文件

本节书摘来华章计算机《深入理解Elasticsearch&#xff08;原书第2版&#xff09;》一书中的第2章 &#xff0c;第2.3.3节&#xff0c;[美]拉斐尔酷奇&#xff08;Rafal Ku&#xff09; 马雷克罗戈任斯基&#xff08;Marek Rogoziski&#xff09;著 张世武 余洪淼 商旦 译 …