对于我们需要查看型别推导结果的时候,主要分为三个阶段:写代码阶段,编译阶段,运行时阶段。
IDE编辑器
IDE编辑器基本上都具有在鼠标悬停在变量或者auto
等字段上会自动推导类型的功能。如下代码所示:
const int theAnswer = 42;
auto x = theAnswer;
auto y = &theAnswer;
IDE编辑器会显示出,x的型别推导结果是int,y的是 const int &。
编译器诊断信息
一种另辟蹊径的方式,是故意诱发一个错误,然后让报错提示出型别信息。常用的手法如下代码:
template<typename T> //只声明TD而不定义
class TD; //TD是"型别显示类"的缩写(Type Display)
有了这个工具,只要试图具象化该模板,就会诱发一个错误,从而达到让编译器告知型别推导结果的作用,示例如下:
TD<decltype(x)> xType; //诱发包括x和y的型别错误信息
TD<decltype(y)> yType;
这段代码编译的时候,会报告如下问题:
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
运行时输出
C++提供了这样的方式,可以输出某个变量的类型。
std::cout << typeid(x).name() << std::endl;
std::cout << typeid(y).name() << std::endl;
但这种方式有一个弊端,输出的字符不是那么好理解,并且每家编译器的格式都不一样,源于C++委员会并未强制定义需要输出什么样的格式。具体不好理解的字符就不展示了,大家有兴趣可以自己尝试,这里只讲要点
看下面一个例子:
template<typename T>
void f(const T& param)
{
using std::cout;
cout << "T = " << typeid(T).name() << '\n'; //这里GNU和Clang,VS都会显示出Widget const *的型别输出
cout << "param = " << typeid(param).name() << '\n'; //这里GNU和Clang,VS都会显示出Widget const *的型别输出
}
std::vector<Widget> createVec();
const auto vw = createVec();
if (!vw.empty()) {
f(&vw[0]);
...
}
这样的输出看似一致,但实际上却是错误的。回忆一下之前的模板型别推导,在模板f中,param被声明称型别const T&。那么如果T是int,param的型别应该就是const int&才对。
很不幸的是,std::type_info::name并不可靠,更加可怕的是,这种不正确的输出结果是符合标准的。
而且更加不幸的是,IDE显示的型别信息也并不可靠。还是上面那份代码,某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类型不易理解,param的型别虽然很短,但是有让人困惑的 ...
存在,其实...
就是T的类型的意思。
那么,std::type_info不好用,IDE也不好用,到底有什么好用的呢,其实是有的——Boost的TypeIndex库
Boost.TypeIndex
不仅仅是跨平台的,开源的,并且不受编译器解析限制的稳定。上述代码例子用Boost.TypeIndex
实现如下:
#include <boost/type_index.hpp>
template<typename T>
void f(const T& param)
{
using std::cout;
using boost::typeindex::type_id_with_cvr;
cout << "T = " << type_id_with_cvr<T>.pretty_name() << '\n';
cout << "param = " << type_id_with_cvr<decltype(param)>.pretty_name() << '\n';
}
std::vector<Widget> createVec();
const auto vw = createVec();
if (!vw.empty()) {
f(&vw[0]);
...
}
这时,无论GNU,Clang,还是VS,均会输出
T = Widget const *
param = Widget const* const &
无论IDE,编译器错误消息,还是Boost.TypeIndex库,都只是理解型别推导的辅助工具。他们十分有用,但还是需要写代码者自己对Item1
~ Item3
有深入理解的前提下。
要点速记 |
---|
1. 利用IDE编辑器,编译器错误消息,Boost.TypeIndex库常常能够辅助获得型别推导结果。 |
2. 上述工具有时候并不完全正确,自身能理解C++型别推导是非常重要的。 |