#ant

The dreams in which I'm dying are the best I've ever had...

为什么不要特化函数模版

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

posted on 2007-08-30 13:55 蚂蚁终结者 阅读(3864) 评论(4)  编辑 收藏 引用 所属分类: C++

Feedback

# re: 为什么不要特化函数模版 2007-08-30 16:54 minidxer

不错  回复  更多评论   

# re: 为什么不要特化函数模版 2007-08-31 09:01 calmman

不错!  回复  更多评论   

# re: 为什么不要特化函数模版 2007-09-03 13:16 ymmol

如果那个特化函数有交代清楚,那就没那么多问题了:
template<>
void f<int>(int arg){
//...
}

template<>
void f<int *>(int *arg){
//...
}

在调用时写清楚也会少些疑惑:
int ia=1;
f<int>(ia);
f<int *>(&ia);
所以我觉得重载或特化都可以,只要你写清楚了,都没事  回复  更多评论   

# re: 为什么不要特化函数模版 2007-09-03 17:08 蚂蚁终结者

@ymmol
是啊,不过有像我这样懒的程序员,习惯了将参数推导交给编译器,也习惯了make_pair类似的写法。  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理