引子
本条目就相当轻松了,主要是S.M.老爷子教大家如何查看类型,毕竟之前3节讲了这么多,对于C++1x的新手来说上手还是要花一段时间的,所以从下面3个角度介绍如何查看类型:
- IDE
- 编译器时诊断
- 运行时输出
正文
正文中,我们需要获得下面两个变量的类型:
const int theAnswer = 42;
auto x = theAnswer;
auto y = &theAnswer;
1.IDE
IDE是最简单的获取方式,现在大部分的IDE都可以帮助用户获取变量类型。这在大部分时候是有效的,但是当类型比较复杂时,可能你的IDE就无法给出正确的结果了。
2.编译器诊断
利用编译时的错误信息,也可以帮助我们诊断类型。我们先声明这样一个类:
template<typename T> // declaration only for TD;
class TD; // TD == "Type Displayer"
注意到这个类并没有被定义,当然也就没有构造器了,那么我们在初始化一个该类的对象例如:
TD<decltype(x)> xType; // elicit errors containing
TD<decltype(y)> yType; // x's and y's types
编译上面的代码时,编译器一定会报错,例如:
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
这样我们就获得了x,y
的类型。
3.运行时获取
运行时获取的好处在于我们可以控制输出格式,但是如何获得你想要的输出格式是一个挑战。我们先从最简单的方法开始:
std::cout << typeid(x).name() << '\n'; // display types for
std::cout << typeid(y).name() << '\n'; // x and y
不同的编译器运行时输出不同的结果,例如gnu和clang会输出x
是”i“以及y
是”PKi“;而微软的编译器则会分别输出”int“和”int const *“。目前为止,一切都好,可能你以为已经OK了。但是实际上考虑到如下代码:
template<typename T> // template function to be called
void f(const T& param);
std::vector<Widget> createVec(); // factory function
const auto vw = createVec(); // init vw w/factory return
if (!vw.empty()) {
f(&vw[0]); // call f
...
}
如果你调用typeid
来输出函数f
中T
和param
的类型,不论哪个编译器,你都会得到错误的结果。例如微软的编译器会告诉你:二者的类型都是class Widget const *
。但我们观察f
的定义可以看出,显然param
的类型应该比T
多一个const &
。这是由于我们这里typeid是传值的,在用模板规则推导类型时,由Item 1可知会丢失const T&
中的const
和引用属性。
所以最后我们需要祭出大杀器boost
库,即里面的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;
// show T
cout << "T = "
<< type_id_with_cvr<T>().pretty_name()
<< '\n';
// show param's type
cout << "param = "
<< type_id_with_cvr<decltype(param)>().pretty_name()
<< '\n';
}
最后我们运行会输出我们想要的结果:
T = class Widget const *
param = class Widget const * const &
总结
最后简单的两条tips:
1.类型推导可以通过IDE,编译器错误信息,以及Boost.TypeIndex库
2.某些工具的结果不一定准确或者难以阅读,所以理解C++类型推导规则仍然很重要