前言
一般来说有三个阶段可以进行类型推衍结果查看:撰写期、编译期、运行期。
IDE
IDE通常可以在完成解析的前提下帮你判断当前类型推衍所得到的结果,但对于较为复杂的类型可能会失效。
编译期诊断
最有效的让编译器显示其推衍结果的方法是故意错用这些类型,编译器所给出的错误信息将清楚地告诉你哪里发生了错误。
问题实例
现有需要作类型推衍的变量x、y:1
2
3const int theAnswer = 42;
auto x = theAnswer;
auto y = &theAnswer;
首先我们需要定义一个类以展示类型:1
2template<typename T>
class TD;//Type Displayer
任何试图实例化该类的操作都将引发错误,因为TD并没有定义。如果需要查看x、y的推衍结果,我们只需要用其类型去实例化TD:1
2TD<decltype(x)> xType;
TD<decltype(x)> yType;
以上操作会产生报错信息如下所示:1
2error: aggregate 'TD<int> xType' has incomplete type and cannot be defined
error: aggregate 'TD<const int *> yType' has incomplete type and cannot be defined
至此完成了编译期类型判别。
运行期输出
有人会想到使用typeid与std::type_info::name来输出对象格式:1
2std::cout << typeid(x).name() << '\n';
std::cout << typeid(y).name() << '\n';
但这种方法有一个缺陷,因为typeid必须作用于一个可以生成std::type_info对象的对象x或y,且type_info对象必须要有一个返回const char*的name成员函数。
对std::type_info::name的调用并不保证返回能够令你读懂的消息,据本书作者所言,微软公司提供的反馈较为直白。
即使已经得到了正确的类型,也可能会有一些其他问题存在。举例而言:1
2
3
4
5
6
7
8
9
10template<typename T>
void f(const T& param);
std::vector<Widget> createVec();
const auto vw = createVec();
if(!vw.empty()){
f(&vw[0]);
}
为了在运行期明确T与param的类型,我们可以定义f如下所示:1
2
3
4
5
6template<typename T>
void f(const T& param){
using std::cout;
cout << "T = " << typeid(T).name() << std::endl;
cout << "param = " << typeid(param).name() << std::endl;
}
微软编译器将给出结果如下:1
2T = class Widget const *
param = class Widget const *
显然,这并非正确的结果,假若T为int型,那么param必然为const int&才对。发生这样错误的原因在于std::type_info::name规格认为模板参数以值传递的方式传递给模板函数,这导致了referenceness与constness、volatileness被丢失。
此外,IDE所推衍得到的类型也未必可靠,至少有时候帮助不大,例如以下这个类型T:1
2const std::_Simple_types<std::_Wrap_alloc<std::_Vec_base_types<Widget,
std::allocator<Widget> >::_Alloc>::value_type>::value_type *
但IDE却推断param的类型是:1
const std::_Simple_types<...>::value_type *const &
中间的…省略号省略了所有关于T类型。
Boost
IDE与type_info::name可能会出错,但Boost不会出错,并且Boost库支持跨平台运行,使用它和使用标准库一样方便,以下将给出使用Boost完成类型推衍的过程:1
2
3
4
5
6
7
8
9
10
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() << endl;
cout << "param = "<< type_id_with_cvr<decltype(param)>().pretty_name() << '\n';
}
它的工作方式是函数模板boost::typeindex::type_id_with_cvr接受一个类型参数并且在推衍结果中不移除const,volatile或reference(with_cvr)。
总结
- 你可以使用IDE、错误信息、Boost库来查看类型推衍。
- 上述工具可能会产生错误,因此熟练Item1-3所描述的法则才是最重要的。