为了更好的理解traits技法。我们一步一步的深入。先从实际写代码的过程中我们遇到诸如下面伪码说起。

 1 template< typename T,typename B>
 2 void (T a,B c){
 3   if(变量a 属于类型b){
 4      //执行相应的代码
 5     a+=c;
 6   } else if(变量a 属于类型c){
 7   //执行相应的代码    
 8     a-=c;
 9   }
10 }

虽然这样的代码可以执行。但是有一点不好的地方:
(1)变量a的类型是在运行期间才会知道的。这样就会导致if和else if对应的执行代码都会编译到可执行文件中,导致编译后代码量增大。
为了更好的理解上述缺点。我们首先引入一段代码(必须看明白了 否则后面的不好理解了)。来说明上述代码是在运行时刻才会知道变量的类型的。

 1 #include<iostream>                                                                                                                                 
 2 using namespace std;
 3 
 4 //声明两种类类型
 5 struct Typeone{
 6   //判断是否是该类类型
 7   static const int typeFlag = 1;
 8 };
 9 struct Typetwo{
10   //判断是否是该类类型
11   static const int  typeFlag = 2;
12 };
13 /****************文字解释的地方*****************/
14 template< typename T >
15 void _func(T a ){
16     if( 1 ==  a.typeFlag ){            //@ 在运行期间才会确定a是哪个类型。
17       cout<<"接下来是任务1要做的事!"<<endl;
18     //具体要执行的代码
19      }
20     else if(2 ==  a.typeFlag ){            //@ 在运行期间才会确定a是哪个类型。    
21         cout<<"接下来是任务2要做的事情!"<<endl;
22     //具体要执行的代码
23      }
24 
25 }
26 /*************************************************/
27 
28 //主函数
29 int main(int argc ,char ** argv)
30 {
31   Typeone b;
32   Typetwo d;
33   _func( b );
34   _func( d );
35   
36   return 0;
37 }

上述代码运行结果:

C++ traits技法的一点理解

  正如代码中标记@ 这个符号的地方所写。变量a是在运行期间通过if的判断才会确定a是哪个类型。虽然我们知道模板函数会对变量a的类型进行推导。也就是说在编译时刻就会把a推导为相应的类型。但是a.typeFlag与1的比较是在运行期,取出a.typeFlag变量的内容然后与1进行比较,当满足这个if条件后才会执行相应的代码。不知道你有没有发现一个问题。在编译程序的时候。if 和else if 对应的执行代码都会被编译到可执行文件中。这也就使得编译后的代码量增大。
上述代码和文字解释了最初那段伪码的缺点。为了解决上述问题我们有四个方案可供选择。
(1)直接函数重载
(2)声明内嵌类型或者叫做迭代器+函数重载
(3)Typeone和Typetwo类型以及内置类型转化为内嵌类型(普通迭代器)+函数重载
(4)traits技法 + 函数重载
先做一些准备工作,插入下面要说的名词代表的意思:

控制函数:

1 //控制函数(利用模板函数的参数推导功能)
2 template< typename T >
3 void _func(T a ){        //@@改变处        
4     T b;
5     func(a,b);
6 }

Typeone类型对应的执行函数:

1 /*Typeone类型执行函数*/
2 template<typename T>
3 void func( T&t,Typeone ){
4     cout<<"接下来是Typeone要做的事!"<<endl;;
5     //具体要执行的代码
6 }

Typetwo类型对应的执行函数:

1 /*Typetwo类型执行函数*/
2 template<typename T>
3 void func( T&t,Typetwo ){
4     cout<<"接下来是Typetwo要做的事情!"<<endl;
5     //具体要执行的代码
6 }

int类型对应的执行函数:

1 /*int 类型执行函数*/
2 template<typename T>
3 void func(T&t,int){
4     cout<<"接下来是int要做的事情!"<<endl;
5     //具体要执行的代码
6 }

好了接下来进入正题。首先举一个例子来说明第一种方法:将上面代码修改为三个重载的模板函数,外加一个控制这些函数的模板函数即控制函数。如下代码以及运行结果图:

方法(1):直接函数重载:

 1 #include<iostream>                                                                                                                                 
 2 using namespace std;
 3 /*************************第二段代码***********************/
 4 //声明两种类类型
 5 struct Typeone{
 6   //判断是否是该类类型
 7   static const int typeFlag = 1;
 8 };
 9 struct Typetwo{
10   //判断是否是该类类型
11   static const int  typeFlag = 2;
12 };
13 /*******************相比第一段代码更改处**********************/
14 //不同的重载函数 
15 /*Typeone类型执行函数*/
16 template<typename T>
17 void func( T&t,Typeone ){
18     cout<<"接下来是Typeone要做的事!"<<endl;;
19     //具体要执行的代码
20 }
21 
22 /*Typetwo类型执行函数*/
23 template<typename T>
24 void func( T&t,Typetwo ){
25     cout<<"接下来是Typetwo要做的事情!"<<endl;
26     //具体要执行的代码
27 }
28 
29 /*int 类型执行函数*/
30 template<typename T>
31 void func(T&t,int){
32     cout<<"接下来是int要做的事情!"<<endl;
33     //具体要执行的代码
34 }
35 
36 //控制函数(利用模板函数的参数推导功能)
37 template< typename T >
38 void _func(T a ){        //@@改变处        
39     T b;
40     func(a,b);
41 }
42 /*************************************************/
43 
44 //主函数
45 int main(int argc ,char ** argv)
46 {
47   Typeone b;
48   Typetwo d;
49   int c;
50 
51   _func( b );
52   _func( d );
53   _func(c);
54   
55   return 0;
56 }

上述代码运行结果图:

C++ traits技法的一点理解

  从代码上可以看出,在main主函数中,当我们分别输入Typeone、Typetwo、 int类型的变量时,都会通过控制重载函数去执行相应的函数,这样一来我们在编译的时刻就会减少了很多的代码(利用重载函数),并且在编译时刻就会知道控制函数中变量a b的类型(利用模板函数的参数类型推导功能)。到了这里就结束了吗?当然不是,如果到了这里就结束的话题目就没有必要说traits技法了。慢慢来。我们在更进一步的说明。从上面的代码可以看出,它对于内置类型和类类型还是有很强的适应性。试想一下。如果我们的需求变了,当我们要求控制函数_func()输入一个迭代器类型的变量,然后它对应的执行函数是迭代器内部类型对应的执行函数。比如myiterator<int> a ,我们要求_func(a)执行int类型对应的func()函数。目前的代码是不能够适应这个需求的。比如下面折叠的代码中就会出现编译错误,注意看主函数中增加的部分以及相比第二段代码更改处(代码中已标记),(因为e是一个迭代器,所以控制函数中T推导为myiterator<int>类型。然后调用函数func(a,b)时候,因为没有这种重载函数会编译出错,而且它本身也不符合我们要求的那样去调用int对应的执行函数)。错误代码如下:

 1 #include<iostream>                                                                                                                                 
 2 using namespace std;
 3 /*************************第三段代码***********************/
 4 //声明两种类类型
 5 struct Typeone{
 6   //判断是否是该类类型
 7   static const int typeFlag = 1;
 8 };
 9 struct Typetwo{
10   //判断是否是该类类型
11   static const int  typeFlag = 2;
12 };
13 /*******************相比第二段代码更改处**********************/
14 template<typename T>
15 struct myiterator{
16     typedef  T type;
17 };
18 /*************************************************/
19 
20 //不同的重载函数 
21 /*Typeone类型执行函数*/
22 template<typename T>
23 void func( T&t,Typeone ){
24     cout<<"接下来是Typeone要做的事!"<<endl;;
25     //具体要执行的代码
26 }
27 
28 /*Typetwo类型执行函数*/
29 template<typename T>
30 void func( T&t,Typetwo ){
31     cout<<"接下来是Typetwo要做的事情!"<<endl;
32     //具体要执行的代码
33 }
34 
35 /*int 类型执行函数*/
36 template<typename T>
37 void func(T&t,int){
38     cout<<"接下来是int要做的事情!"<<endl;
39     //具体要执行的代码
40 }
41 
42 //控制函数(利用模板函数的参数推导功能)
43 template< typename T >
44 void _func(T a ){        //@@改变处        
45     T b;
46     func(a,b);
47 }
48 
49 //主函数
50 int main(int argc ,char ** argv)
51 {
52   Typeone b;
53   Typetwo d;
54   int c;
55   myiterator<int> e;    //增加的部分
56 
57   _func( b );
58   _func( d );
59   _func(c);
60   _func(e);        //增加的部分
61 
62   return 0;
63 }

  针对上面的错误代码,那么有没有其他方法能够解决呢?我们传统的思想是在控制函数内部在加一层判断是不是迭代器类型,之后在调用迭代器内部类型对应的执行函数。那么这样不仅是回到了原来伪码出现的问题(编译后的代码量会增加)。而且还修改了控制函数本身的结构。这破坏了控制函数代码的封装性(因为每加入一种类型就要变动函数内部的写法,这本身不符合函数封装性,而且影响了函数内部算法本身的适应性)。那么我们该如何更改呢?看了上述错误代码后你可能会想,我可以把控制函数内的T b 变为typename T::type_category b 这样就能很好的解决了上述的额外要求了!比如下面的折叠代码(主要看控制函数部分):

 1 #include<iostream>                                                                                                                                 
 2 using namespace std;
 3 /*************************第三段代码***********************/
 4 //声明两种类类型
 5 struct Typeone{
 6   //判断是否是该类类型
 7   static const int typeFlag = 1;
 8 };
 9 struct Typetwo{
10   //判断是否是该类类型
11   static const int  typeFlag = 2;
12 };
13 /*******************相比第二段代码更改处**********************/
14 //迭代器或者内嵌类型
15 template<typename T>
16 struct myiterator{
17     typedef  T type_category;
18 };
19 /*************************************************/
20 
21 //不同的重载函数 
22 /*Typeone类型执行函数*/
23 template<typename T>
24 void func( T&t,Typeone ){
25     cout<<"接下来是Typeone要做的事!"<<endl;;
26     //具体要执行的代码
27 }
28 
29 /*Typetwo类型执行函数*/
30 template<typename T>
31 void func( T&t,Typetwo ){
32     cout<<"接下来是Typetwo要做的事情!"<<endl;
33     //具体要执行的代码
34 }
35 
36 /*int 类型执行函数*/
37 template<typename T>
38 void func(T&t,int){
39     cout<<"接下来是int要做的事情!"<<endl;
40     //具体要执行的代码
41 }
42 
43 //控制函数(利用模板函数的参数推导功能)
44 template< typename T >
45 void _func(T a ){        //@@改变处        
46     typename T::type_category b;
47  //   T b;
48     func(a,b);
49 }
50 
51 //主函数
52 int main(int argc ,char ** argv)
53 {
54   Typeone b;
55   Typetwo d;
56   int c;
57   myiterator<int> e;
58   
59   /*相比第三段代码更改处*/
60   _func( b );        //注释掉
61   _func( d );
62   _func(c);
63   _func(e);
64 
65   return 0;
66 }
View Code

相关文章:

  • 2021-11-15
  • 2022-12-23
  • 2022-12-23
  • 2021-10-17
  • 2022-12-23
  • 2022-03-03
  • 2021-08-24
  • 2021-11-05
猜你喜欢
  • 2021-08-23
  • 2021-09-21
  • 2022-12-23
  • 2021-05-30
  • 2022-01-10
相关资源
相似解决方案