写点什么

查看自动类型推导结果的方法

作者:爱分享
  • 2024-04-13
    广东
  • 本文字数:2894 字

    阅读完需:约 9 分钟

查看自动类型推导结果的方法

《深入解析C++的auto自动类型推导》《深入解析decltype和decltype(auto)》两篇文章中介绍了使用 auto 和 decltype 以及 decltype 和 auto 结合来自动推导类型的推导规则和用法,虽然确定类型的事情交给编译器去做了,但是在有的时候我们可能还是想知道编译器推导出来的类型具体是什么,下面就来介绍几种获取类型推导结果的方法,根据开发的不同阶段,你可以在不同阶段采用不同的方法,比如在编写代码时,编译代码时,代码运行时。

利用 IDE 查看

当你在编写代码的过程中想查看一下某个变量推导出来的类型是什么,做到心中有数,其实在 IDE 中就可以直接查看,现在的 IDE 都比较智能,如微软的 Visual Studio 和目前比较流行的跨平台编辑器 VS Code 都有此功能。你只要将鼠标移到想要查看的那个变量上面,就会弹出这个变量的类型,不过要让 IDE 能够推导出代码中变量的类型,你的代码至少要没有语法错误,因为 IDE 会静态分析你代码来推导出这些类型,如下面的代码:

int a;auto x = a;auto& y = a;
复制代码

你把鼠标移动 x 上面,则会弹出显示“int x”,把鼠标移动 y 上面,就会弹出显示“int& y”。对于 C++的内置类型,IDE 基本上都能推导出来,但是遇到比较复杂的类型或者复杂的代码上下文中,IDE 可能就有点不够智能了。

借助工具查看

当 IDE 不能正确显示出变量的类型的时候还可以选择借助外部的工具来查看,这里推荐一个在线工具,地址是:https://cppinsights.io,这是一个基于 Clang 的工具,对用户所写的 C++代码转换成最终形式的 C++代码,有点类似于 C/C++的预处理器一样,把一些宏代码替换成真实的代码一样,但它的功能更进一步也更强大,该工具支持基于范围的循环、结构化绑定、生成默认构造函数、初始化列表、auto 与 decltype 转换成真实类型,最强大的是会生成模板实例化后的代码,这些功能对于调试 C++代码非常有用。使用的界面如下:



(点击查看大图)

左边是我们输入的原始代码,输入结束之后点击左上角的三角形按钮,就会生成右边经过转换后的代码,可以看到右边中已经将类型别名 T1 到 T10 等的类型转换成具体的类型了,使用时可以在上面的下拉列表框中选择不同的 C++标准。

需要注意的是,这个工具我发现了一个 Bug,就是上面代码中的 T9 类型别名,正确的类型应该是 func 函数的类型:int(int, int),这里显示为它的返回值的类型了。

编译时打印

编译器肯定是知道变量的类型的,但是它没法直接告诉你,有一个可以让编译器告诉你的办法,就是编译发生错误时编译器在报告的错误信息中肯定会提到导致此错误的类型,因此我们可以声明一个如下的模板:

template<typename T>class dumpType;
复制代码

因为上面的模板只有声明,没有具体的定义,因此如果要实例化这个模板就会导致一个编译错误。所以我们想要查看哪个变量的类型,只要将这个变量的类型作为模板的形参去实例化它,就会导致一个错误,在编译器给出的错误信息里就会显示出这个变量的具体类型,如下所示:

const int x1 = 1;auto j = x1;dumpType<decltype(j)>{};
复制代码

编译时发生错误,其中输出的错误信息含有这一行:

error: implicit instantiation of undefined template 'dumpType<int>'
复制代码

dumpType<int>中尖括号内的 int 就是 j 的类型了,以此类推,只要将想要查看的变量替换到上面的参数中就可以了。但是这里有一个缺点,就是每次只能查看一个变量的类型,需要查看多个变量时就显得繁琐。好在 C++11 标准引入了支持可变参数的模板特性,我们可以利用这个特性来完善上面的功能,将上面的模板修改一下:

template<typename... Ts>class dumpType;
复制代码

现在可以一次传递多个参数给此模板,如下面的例子:

template<typename... Ts>class dumpType;
int func(int, int) { int x; return x;}
class Base {public: int x = 0;};
int main() { const Base b; const int ci = 1; auto x = ci; using T1 = decltype(x); using T2 = decltype((x)); using T3 = decltype(b.x); using T4 = decltype((b.x)); using T5 = decltype(func); dumpType<T1, T2, T3, T4, T5>{};}
复制代码

编译时将输出以下的错误信息:

error: implicit instantiation of undefined template 'dumpType<int, int &, int, const int &, int (int, int)>'
复制代码

运行时输出

有时我们想要在代码运行的时候输出某些变量的类型,这时候可以借助 C++的 RTTI 特性,C++标准库提供了 typeid 函数和 type_info 类,对变量或者类型调用 typeid 会返回一个 type_info 对象,type_info 类里有一个成员函数 name,这个函数返回一个 const char*类型的名称,但这个名称一般都经过 C++的混淆,比较不易看懂,如以下的代码:

auto add (auto p1, auto p2) { return p1 + p2; };auto d = add(1, 2.0);printf("type of d is %s\n", typeid(d).name());auto s = add("hello"s, "world"s);printf("type of s is %s\n", typeid(s).name());
复制代码

输出的结果是:

type of d is dtype of s is NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
复制代码

输出结果中的 d 代表的是 double 类型,如 int 类型的话则显示 i,std::string 类型的原型比较复杂,所以输出来的结果比较难看懂。但这种方法最大的缺点是功能不太完善,比如对于引用类型它无法正确的显示出来,比如下面的代码:

int i = 1;auto& j = i;printf("type of j is %s\n", typeid(j).name());
复制代码

变量 j 正确的类型应该是 int&,但是上面的输出结果是 i,是 int 类型,估计是 j 作为参数传给 typeid 函数的时候是作为值传递的,丢失了引用属性,在这里 CV 修饰词也会被忽略掉,如在上面定义变量 j 时加上 const 修饰,但输出结果还是 int 类型。

这时可以采用另外一种手段来输出变量的类型,跟上小节中的例子一样借助模板的技术,实现一个模板函数,在模板函数中利用编译器提供的宏,把这个函数的原型打印出来,函数原型中就包含了函数的参数个数及其类型,这个宏由于不是 C++标准中定义的,是由各编译器扩展的,因此名称不一样,在 GCC/Clang 中是__PRETTY_FUNCTION__,在微软的 MSVC 中是__FUNCSIG__,如下代码:

#include <iostream>
template<typename... Ts>void dumpType() { // GCC/Clang使用这行 std::cout << __PRETTY_FUNCTION__ << std::endl; // MSVC则使用下面这行 //std::cout << __FUNCSIG__ << std::endl;};
int func(int, int) { int x; return x;}
class Base {public: int x = 0;};
int main() { const Base b; const int ci = 1; auto x = ci; using T1 = decltype(x); using T2 = decltype((x)); using T3 = decltype(b.x); using T4 = decltype((b.x)); using T5 = decltype(func); dumpType<T1, T2, T3, T4, T5>();}
复制代码

各个编译器输出的结果是:

// Clangvoid dumpType() [Ts = <int, int &, int, const int &, int (int, int)>]// GCCvoid dumpType() [with Ts = {int, int&, int, const int&, int(int, int)}]// MSVCvoid __cdecl dumpType<int,int&,int,const int&,int(int,int)>(void)
复制代码



此篇文章同步发布于我的微信公众号:查看自动类型推导结果的方法

如果您感兴趣这方面的内容,请在微信上搜索公众号 iShare 爱分享或者微信号 iTechShare 并关注,或者扫描以下二维码关注,以便在内容更新时直接向您推送。


公众号:iShare爱分享


发布于: 刚刚阅读数: 5
用户头像

爱分享

关注

还未添加个人签名 2024-04-08 加入

还未添加个人简介

评论

发布
暂无评论
查看自动类型推导结果的方法_编辑器_爱分享_InfoQ写作社区