1. 程式人生 > >為什麼不要特化函式模版

為什麼不要特化函式模版

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 template

s能夠過載我們可以通過過載來實現和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 staticint apply(const T & t) {
 6 return0
;
 7 
    }
 8 
};
 9 
10 //partial specialazation for int 11 template <> 12 struct FuncImpl<int>  {
13 staticint apply(int
 t) {
14 return1
;
15 
    }
16 
};
17 
18 //partial specialazation for T* 19 template <class T> 20     struct FuncImpl<*>  {
21 staticint apply(T *
t) {
22 return2
;
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 螞蟻終結者 閱讀(3683) 評論(4)  編輯 收藏 引用 所屬分類: C++