為什么不要特化函數模版
在C++中有class templates 和function templates,這兩種模版有很多區別,最重要的區別就是重載(overloading):
普通的C++類不能重載,當然類模版也不能重載;相反,普通函數可以重載,函數模版也能重載。這再正常不過,看下面的代碼:
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 templates 和function templates有很大的區別:一個class template 能夠被partially specialized and/or
fully specialized,一個function template只能被fully specialized,但是由于function templates能夠重載我們可以通過重載來實現和partially specialized 相當的功能。下面的代碼說明了這些區別:
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
根據函數重載解析規則:
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.考慮如下代碼:
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.再考慮下面的代碼:
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的重載當然沒問題)。如果一定要這樣,可以使用如下方法模擬函數模板的偏特化:
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<T *> {
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 r = func('c'); //r = 0
34 r = func(8); //r = 1
35 r = func(&i); //r = 2
posted on 2008-10-13 11:26 肥仔 閱讀(297) 評論(0) 編輯 收藏 引用 所屬分類: C++ 模板