Overloading vs. Specialization
在C++中有class templates 和function 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 templates 和function 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<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