为什么不要特化函数模版

为什么不要特化函数模版

Overloading vs. Specialization

在C++中有class templatesfunction templates,这两种模版有很多区别,最重要的区别就是重载(overloading):
普通的C++类不能重载,当然类模版也不能重载;相反,普通函数可以重载,函数模版也能重载。这再正常不过,看下面的代码:

 1  //  Example 1: Class vs. function template, and overloading 
 2  //
 3 
 4  //  A class template
 5  template < class  T >   class  X {  /*... */  };       //  (a)
 6 
 7  //  A function template with two overloads
 8  template < class  T >   void  f( T );               //  (b)
 9  template < class  T >   void  f(  int , T,  double  );  //  (c)
10 

像上面未特化的模板通常叫做base templates。当然,base templates能够被特化,在特化这一点上
class templatesfunction templates有很大的区别:一个class template 能够被partially specialized and/or
fully specialized,一个function template只能被fully specialized,但是由于function templates能够重载我们可以通过重载来实现和partially specialized 相当的功能。下面的代码说明了这些区别:

 1  //  Example 1, continued: Specializing templates 
 2  //
 3 
 4  //  A partial specialization of (a) for pointer types 
 5  template < class  T >   class  X < T *>  {  /*... */  };
 6 
 7  //  A full specialization of (a) for int 
 8  template <>   class  X < int >  {  /*... */  };
 9 
10  //  A separate base template that overloads (b) and (c) 
11  //  -- NOT a partial specialization of (b), because 
12  //  there's no such thing as a partial specialization 
13  //  of a function template! 
14  template < class  T >   void  f( T *  );              //  (d)
15 
16  //  A full specialization of (b) for int 
17  template <>   void  f < int > int  );               //  (e)
18 
19  //  A plain old function that happens to overload with 
20  //  (b), (c), and (d) -- but not (e), which we'll 
21  //  discuss in a moment 
22  void  f(  double  );                            //  (f)
23 

根据函数重载解析规则:

 1  //  Example 1, continued: Overload resolution 
 2  //  
 3  bool b; 
 4  int  i; 
 5  double  d;
 6 
 7  f( b );         //  calls (b) with T = bool 
 8  f( i,  42 , d );  //  calls (c) with T = int 
 9  f(  & i );        //  calls (d) with T = int 
10  f( i );         //  calls (e) 
11  f( d );         //  calls (f)

上面说的这些其实都是很简单的情况,大多数人很容易就能明白,下面的才是容易让人弄混的:

1.考虑如下代码:

 1  //  Example 2: Explicit specialization 
 2  //  
 3  template < class  T >   //  (a) a base template 
 4  void  f( T );
 5 
 6  template < class  T >   //  (b) a second base template, overloads (a) 
 7  void  f( T *  );      //      (function templates can't be partially 
 8                     //      specialized; they overload instead)
 9 
10  template <>          //  (c) explicit specialization of (b) 
11  void  f <> ( int * );
12 
13  //  ...
14 
15  int   * p; 
16  f( p );            //  calls (c)

最后一行的结果像大多数人所期望的一样,问题是:为什么期望是这个结果?
如果你期望的原因是错误的,接下来的一定会让你好奇。也许你会说:"我为int*写了一个特化版本,f(p)当然会调用c",不幸的是,这正是错误的原因!!!

2.再考虑下面的代码:

 1  //  Example 3
 2  //  
 3  template < class  T >   //  (a) same old base template as before 
 4  void  f( T );
 5 
 6  template <>          //  (c) explicit specialization, this time of (a)
 7  void  f <> ( int * );
 8 
 9  template < class  T >   //  (b) a second base template, overloads (a) 
10  void  f( T *  );
11 
12  //  ...
13 
14  int   * p; 
15  f( p );            //  calls (b)! overload resolution ignores 
16                     //  specializations and operates on the base 
17                     //  function templates only

如果这个结果让你感到惊奇,那就对了!很多人都会感到惊奇!
理解这个的关键是:Specializations don't overload,only the base templates overload.

重载解析仅仅选择base template(或者nontemplate function,如果有的话),只有当编译器已经决定了哪个
base template将会被选择,编译器才会继续往下寻找适合的特化版本,如果找到了就使用那个特化版本。

最后,应当避免特化函数模板,也要避免重载函数模板(nontemplate function的重载当然没问题)。如果一定要这样,可以使用如下方法模拟函数模板的偏特化:

 1  // base template class, 
 2  template  < class  T >
 3  struct FuncImpl {
 4       // users, go ahead and specialize this
 5       static   int  apply( const  T  & t) {
 6           return   0 ;
 7      }
 8  };
 9 
10  // partial specialazation for int
11  template  <>
12  struct FuncImpl < int >  {
13       static   int  apply( int  t) {
14           return   1 ;
15      }
16  };
17 
18  // partial specialazation for T*
19  template  < class  T >
20      struct FuncImpl < *>  {
21       static   int  apply(T  * t) {
22           return   2 ;
23      }
24  };
25 
26  // users, don't touch this!
27  template  < class  T >
28  int  func( const  T  & t) {
29       return  FuncImpl < T > ::apply(t);
30  }
31 
32  int  i  =   10 , r;
33  =  func( ' c ' );  // r = 0
34  =  func( 8 );  // r = 1
35  =  func( & i);  // r = 2

你可能感兴趣的:(为什么不要特化函数模版)