[C++基础]049_用模板匹配实现编译期类型甄别
本文,我们将介绍一种利用模板匹配来实现类型甄别的技术。
首先,类型是数值的抽象,那么我们从最简单的编译期数值甄别开始看,如果你写下了下面这样一段代码:
1 int main() 2 { 3 int i = 10; 4 i++; 5 bool b = i > 0 ? true:false; 6 if(b){ 7 cout<<"TRUE"<<endl; 8 }else{ 9 cout<<"FALSE"<<endl; 10 } 11 }
上面的代码,对于b的值来说,是编译期就能确定的,肯定是true。
试想,如果这段代码如果用于处理互联网上的请求,每一次请求都调用上面的代码片段时,响应程序每次都会进行如上“复杂”的运算。
但是,有必要吗?没有必要,上面的运算只需要运算一次就够了,那如何使得只运算一次呢?让程序在编译期决定运算结果,是的。
幸好,目前很多编译器都有这个智能的优化,当代码被汇编为汇编码时,编译器就已经为我么做了优化了,当然,本文并不讨论这个,本文讨论的是数值的抽象,类型的编译期甄别。
为了解编译期甄别,我们先介绍运行期类型甄别的方法,这里用一个关键函数typeid来实现运行期类型甄别,看如下运用typeid函数的代码:
1 int main() 2 { 3 const char* intType = "int"; 4 5 int i = 10; 6 const char* iType = typeid(i).name(); 7 if(!strcmp(intType, iType)){ 8 cout<<"i type is : "<<"int"<<endl; 9 }else{ 10 cout<<"i type is : "<<"unknown"<<endl; 11 } 12 }
上面的代码用typeid函数获取了i的类型,但是必须要等到运行期才能决定,那如何能在编译期就决定呢?让我们来进入今天的主题:用模板匹配实现编译期类型甄别。
首先要明确我们要干什么,我们希望类型在编译期就能判断,数值的抽象是类型,而类型的抽象是模板。于是我们想到用模板来实现。
当我们使用模板的时候,编译期是就已经能决定要使用何种模板的,比如一下代码:
// 函数模板 template <typename T> void foo(T){ cout<<"unknown"<<endl; } // int类型的特化 template <> void foo(int){ cout<<"int"<<endl; } int main() { int i = 10; double d = 12.2; foo<int>(i); foo<double>(d); }
上面当我们调用foo<int>(i);时,可以看到使用的是int类型特化的模板函数。决定该调用哪个函数是在编译期就决定下来的,那我们要如何利用这个特性呢?
1 template<typename T> 2 class ClassChecker 3 { 4 public: 5 typedef struct { char c[2];} Yes; 6 typedef struct { char c[1];} No; 7 static Yes _Check(T t); // 如果类型T和函数的T相同,则调用该函数,返回Yes,即长度为2的结构体 8 static No _Check(...); // 不匹配时,调用该函数,返回No,即长度为1的结构体 9 }; 10 // 这个宏的功能就是利用模板的匹配来看类型与数值是否是同一类型的 11 #define TYPE_CHECKER(T,i) ((sizeof ( ClassChecker<T>::_Check(i)))==2?true:false) 12 13 int main() 14 { 15 int i = 10; 16 if(TYPE_CHECKER(int, i)){ 17 cout<<"type is int"<<endl; 18 } 19 }
上面的代码就运用了模板推演在编译期就确定了数值的类型的,这里不做详细解释,
上面的代码不只可以对基本类型进行推演,对复杂的继承关系的类也能进行编译期推演,如下:
1 template<typename T> 2 class ClassChecker 3 { 4 public: 5 typedef struct { char c[2];} Yes; 6 typedef struct { char c[1];} No; 7 static Yes _Check(T t); 8 static No _Check(...); 9 }; 10 // 这个宏的功能就是利用模板的匹配来看类型与数值是否是同一类型的 11 #define TYPE_CHECKER(T,i) ((sizeof ( ClassChecker<T>::_Check(i)))==2?true:false) 12 13 class A{}; 14 class B:public A{}; 15 class C:public B{}; 16 class D:public C{}; 17 18 int main() 19 { 20 21 B b; 22 if(TYPE_CHECKER(D,b)) 23 { 24 cout<<"D"<<endl; 25 } 26 else if(TYPE_CHECKER(C,b)) 27 { 28 cout<<"C"<<endl; 29 } 30 else if(TYPE_CHECKER(B,b)) 31 { 32 cout<<"B"<<endl; 33 } 34 else if(TYPE_CHECKER(A,b)) 35 { 36 cout<<"A"<<endl; 37 } 38 }
上面的代码要注意的是,要从底层子类开始推演,否则得不到结果,原因很简单,A的子类可以用A的指针来指向,所以会发生问题。