获取类型推导结果
本文最后更新于:April 20, 2022 am
本博客介绍三种查看类型推导结果的方法
在软件开发的不同阶段,都可以获得类型推导的信息,关键是我们身处于哪个阶段,本博客从编写代码、编译和运行时三个阶段进行介绍
IDE
在编写代码期间,IDE常常会自动地显示类型推导的结果。不过,IDE的这些类型信息是来源于C++编译器,所以如果代码无法正常编译,那么类型推导结果也可能不会出现
1 |
|
对于简单的,诸如int
的类型,这样就够了,但随着类型变得更复杂,IDE的信息就不那么管用了
编译器诊断
想要让编译器暴露某个类型推断结果,一个行之有效的方案是使用那个类型制造编译错误,以此来让编译器提示类型不匹配
如果我们希望让编译器告诉我们上述变量中x
和y
的类型,可以如此操作:
1 |
|
由于我们没有给出类模板的定义,因此编译器将会发出类似这样的错误报告
运行时输出
如果要在控制台输出类型信息,那就必须等到运行时,但是可以自主控制所有的输出格式。这里的难点是要得到我们关心类型的字符串表达,并且这个表示得是易读易理解的
乍一看可以用typeid()
和std::type_info::name
来解决
1 |
|
调用typeid
函数,产生一个std::type_info
对象,随后调用它的name
方法可以得到一个C风格的字符串,用以概括类型信息
但是此函数不保证一定能返回”有意义“的字符串,但是编译器工作者已经在尽力返回有用的信息了。GNU和Clang编译器对于x
会返回“i”表示int
,对y
返回“PKi”,表示“pointer to konst const”,当然,也可以为它们安装C++filt工具来翻译那些晦涩的信息。如果是Microsoft的编译器,将会显示“int”和“int const *”
考虑一个更复杂的情况
1 |
|
这里既有用户自定义的类型Widget
,又有STL容器,还有auto
声明的变量,更接近于现实中我们迫切需要看到编译器的类型推导信息的情景,比如我们会希望知道函数模板f
中的类型参数T
和param
的最终类型
1 |
|
GNU和Clang的编译器环境下,将会输出
1 |
|
已经知道PK代表pointer to const,而此处的6,代表6后面的类名的字符数量(Widget有6个字符),也就是说它们的类型是const Widget*
Microsoft的编译器则是:
1 |
|
等等,T
和param
的类型居然是一样的?在函数模板f
中,param
可是声明为const T&
的呀?
不幸的是,std::type_info::name
的结果不总是可靠的,这就是反例之一。更近一步说,它们在这里必须是错的,因为std::type_info::name
函数声明指出,会像一个按值传递的函数模板一样对待参数。在这篇博客中,我们已经讨论过,当函数模板的参数是按值传递时,如果实参是引用,那么引用的符号将会被忽略;此外,如果移除引用符号之后,还有const
或者volatile
关键字,那么这两者也被一并忽略
param
的类型,在此处为const Widget * const &
,被反射成const Widget*
也就不无道理。首先忽略了其引用符号,随后又忽略了它本身不可变的const
符号(右侧的const
),最后变成了const Widget*
更糟糕的事,IDE在此时报告的类型同样也不可靠,至少说,是不太有用。在此例中,我所用的IDE将T
报告成
1 |
|
对于param
,报告的则是
1 |
|
param
类型看起来比T
的类型更人畜无害一些,但是“…”到底意味着什么呢?其实这是编译器的一种语言,它是在说,”既然T
已经有这么一大堆了,那在param
的类型中我就懒得再显示一次了吧!“
使用boost库
当知道std::type_info::name
和IDE的问题之后,Boost 的TypeIndex库就像一个救星
1 |
|
type_id_with_cvr
能够正常工作的秘密就在于,它可以在不忽略const
、volatile
和引用的情况下,拿到类型参数。函数返回boost::typeindex::type_index
对象,方法pretty_name
返回一个std::string
对象,以提供友好的字符串表示
在GNU和Clang编译器下,使用Boost.TypeIndex来替代type_id
,得到
1 |
|
Microsoft的编译器会返回
1 |
|
令人惊喜的一致!
总结
可以通过IDE、制造编译错误以及使用Boost的TypeIndex库来查看类型推导结果
一些工具的结果不能保证准确性和可用性,最重要的还是要理解C++的类型推导规则
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!