模板的特化与偏特化 ,C++类模板的三种特化

来源:互联网 发布:软件总工程师 编辑:程序博客网 时间:2024/04/27 16:14

模板特化和偏特化

作者:谢宝陵  周 生
(合肥市炮兵学院计算中心 230031)
摘要:本文通过例子介绍了在 C++标准库中广泛使用的模板特化和偏特化,并指出了模板特化和偏特化的定义规则和应用规则。
关键词:模板、特化、偏特化

1.引言


     C++中的模板分为类模板和函数模板,虽然它引进到C++标准中的时间不是很长,但是却得到了广泛的应用,这一点在STL中有着充分的体现。目前,STL在C++社区中得到了广泛的关注、应用和研究。理解和掌握模板是学习、应用和研究以及扩充STL的基础。而STL模板实例中又充斥着大量的模板特化和偏特化。


2.模板的定义


(1) 类模板

定义一个栈的类模板,它可以用来容纳不同的数据类型
说明如下:
template <class T>
class stack {
private:
  list* top;
public:
  stack();
  stack(const stack&);
  ~stack();
  void push(T&);
  T& pop();
  //…
};
上述定义中,template告诉编译器这是一个模板,尖括号中的<class T >指明模板的参数,可以有一个或多个,具体实现时由用户指定,其中template <class T >中的关键字class可以用关键字typename来代替。
类模板的使用除了要在声明时指明模板参数外,其余均与普通的类相同,例如:
stack<int> int_stack;
stack<char> ch_stack;
stack<string> str_stack;
int_stack.push(10);
ch_stack.push(‘z’);
str_stack.push(“c++”);


(2)函数模板
假设现在要定义一个max函数来返回同一类型(这种类型是允许比较的)两个值的最大者.
template<class T>
T mymax(const T& t1,const T& t2)
{ return t1 < t2 ? t2 : t1; }
template <class T>的意义与类模板定义中相同。
模板函数的使用与普通非模板函数使用相同,因为模板函数的参数可以从其传入参数中解析出来。例如:
int highest = mymax(5,10);
char c = mymax(‘a’, ’z’);
mymax(5,10)解析出模板函数参数为int, mymax(‘a’, ’z’)解析出模板函数的参数为char。


3.模板的特化


(1)类模板特化
有时为了需要,针对特定的类型,需要对模板进行特化,也就是特殊处理.例如,stack类模板针对bool类型,因为实际上bool类型只需要一个二进制位,就可以对其进行存储,使用一个字或者一个字节都是浪费存储空间的.
template <class T>
class stack {};
template < >
class stack<bool> { //…// };
上述定义中template < >告诉编译器这是一个特化的模板。


(2) 函数模板的特化
看下面的例子
main()
{
  int highest = mymax(5,10);
  char c = mymax(‘a’, ’z’);
  const char* p1 = “hello”;
  const char* p2 = “world”;
  const char* p = mymax(p1,p2);
}
前面两个mymax都能返回正确的结果.而第三个却不能,因为,此时mymax直接比较两个指针p1 和 p2 而不是其指向的内容.
针对这种情况,当mymax函数的参数类型为const char* 时,需要特化。
template <class T>
T mymax(const T t1, const T t2)
{
   return t1 < t2 ? t2 : t1;
}

template <>
const char* mymax(const char* t1,const char* t2)
{
   return (strcmp(t1,t2) < 0) ? t2 : t1;
}
现在mymax(p1,p2)能够返回正确的结果了。


4.模板的偏特化


模板的偏特化是指需要根据模板的某些但不是全部的参数进行特化


(1) 类模板的偏特化
例如c++标准库中的类vector的定义
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
这个偏特化的例子中,一个参数被绑定到bool类型,而另一个参数仍未绑定需要由用户指定。

(2) 函数模板的偏特化
  严格的来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。
  template <class T> void f(T);  (a)
  根据重载规则,对(a)进行重载
  template < class T> void f(T*);  (b)
  如果将(a)称为基模板,那么(b)称为对基模板(a)的重载,而非对(a)的偏特化。C++的标准委员会仍在对下一个版本中是否允许函数模板的偏特化进行讨论。

5.模板特化时的匹配规则


(1) 类模板的匹配规则
最优化的优于次特化的,即模板参数最精确匹配的具有最高的优先权
例子:
template <class T> class vector{//…//}; // (a)  普通型
template <class T> class vector<T*>{//…//};  // (b) 对指针类型特化
template <>   class vector <void*>{//…//};  // (c) 对void*进行特化
每个类型都可以用作普通型(a)的参数,但只有指针类型才能用作(b)的参数,而只有void*才能作为(c)的参数


(2) 函数模板的匹配规则
非模板函数具有最高的优先权。如果不存在匹配的非模板函数的话,那么最匹配的和最特化的函数具有高优先权
例子:
template <class T> void f(T);  // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*);  // (f)
template <> void f<int> (int) ; // (g)
void f(double);  // (h)
bool b;
int i;
double d;
f(b); // 以 T = bool 调用 (d)
f(i,42,d) // 以 T = int 调用(e)
f(&i) ; // 以 T = int* 调用(f)
f(d);  //  调用(g)


参考文献
[1] Bjarne Stroustrup, The C++ Programming Language (Special Edition), Addison Wesley,2000
[2] Nicolai M.Josuttis, The C++ Standard Library – A Tutorial and Reference ,Addison Wesley,1999
[3] Stanley Lippman and Josée Lajoie ,C++ Primier, 3rd Edition ,Addison Wesley Longman ,1998

 

 

 

转载:http://www.cppblog.com/SmartPtr/archive/2007/07/04/27496.html

C++类模板的三种特化

By SmartPtr(http://www.cppblog.com/SmartPtr/)

 

  说起C++的模板及模板特化, 相信很多人都很熟悉 ,但是说到模板特化的几种类型,相信了解的人就不是很多。我这里归纳了针对一个模板参数的类模板特化的几种类型, 一是特化为绝对类型; 二是特化为引用,指针类型;三是特化为另外一个类模板。
 这里用一个简单的例子来说明这三种情况:

// general version
template<class T>
class Compare
{
public
:
    
static bool IsEqual(const T& lh, const T&
 rh)
    {
        
return lh ==
 rh;
    }
};

这是一个用于比较的类模板,里面可以有多种用于比较的函数, 以IsEqual为例。
 
一、特化为绝对类型
也就是说直接为某个特定类型做特化,这是我们最常见的一种特化方式, 如特化为float, double等

// specialize for float
template<>
class Compare<float>
{
public:
    
static bool IsEqual(const float& lh, const float&
 rh)
    {
        
return abs(lh - rh) < 10e-3
;
    }
};

// specialize for double

template<>
class Compare<double>
{
public:
    
static bool IsEqual(const double& lh, const double&
 rh)
    {
        
return abs(lh - rh) < 10e-6
;
    }
};


 
二、特化为引用,指针类型
这种特化我最初是在stl源码的的iterator_traits特化中发现的, 如下:

template <class _Iterator>
struct iterator_traits {
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

// specialize for _Tp*

template <class _Tp>
struct iterator_traits<_Tp*> {
  typedef random_access_iterator_tag iterator_category;
  typedef _Tp                         value_type;
  typedef ptrdiff_t                   difference_type;
  typedef _Tp
*
                        pointer;
  typedef _Tp
&
                        reference;
};

// specialize for const _Tp*

template <class _Tp>
struct iterator_traits<const _Tp*> {
  typedef random_access_iterator_tag iterator_category;
  typedef _Tp                         value_type;
  typedef ptrdiff_t                   difference_type;
  typedef 
const _Tp*
                  pointer;
  typedef 
const _Tp&
                  reference;
};

 

 当然,除了T*, 我们也可以将T特化为 const T*, T&, const T&等,以下还是以T*为例:

// specialize for T*
template<class T>
class Compare<T*>
{
public:
    
static bool IsEqual(const T* lh, const T*
 rh)
    {
        
return Compare<T>::IsEqual(*lh, *
rh);
    }
};

这种特化其实是就不是一种绝对的特化, 它只是对类型做了某些限定,但仍然保留了其一定的模板性,这种特化给我们提供了极大的方便, 如这里, 我们就不需要对int*, float*, double*等等类型分别做特化了。

三、特化为另外一个类模板

这其实是第二种方式的扩展,其实也是对类型做了某种限定,而不是绝对化为某个具体类型,如下:

// specialize for vector<T>
template<class T>
class Compare<vector<T> >
{
public:
    
static bool IsEqual(const vector<T>& lh, const vector<T>&
 rh)
    {
        
if(lh.size() != rh.size()) return false
;
        
else

        {
            
for(int i = 0; i < lh.size(); ++i)
            {
                
if(lh[i] != rh[i]) return false
;
            }
        }
        
return true
;
    }
};


这就把IsEqual的参数限定为一种vector类型, 但具体是vector<int>还是vector<float>, 我们可以不关心, 因为对于这两种类型,我们的处理方式是一样的,我们可以把这种方式称为“半特化”。

当然, 我们可以将其“半特化”为任何我们自定义的模板类类型:

// specialize for any template class type
template <class T1> 
struct
 SpecializedType
{
    T1 x1;
    T1 x2;
};
template 
<class T>

class Compare<SpecializedType<T> >
{
public:
    
static bool IsEqual(const SpecializedType<T>& lh, const SpecializedType<T>&
 rh)
    {
        
return Compare<T>::IsEqual(lh.x1 + lh.x2, rh.x1 +
 rh.x2);
    }
};


 这就是三种类型的模板特化, 我们可以这么使用这个Compare类:

   // int
    int i1 = 10;
    
int i2 = 10
;
    
bool r1 = Compare<int>
::IsEqual(i1, i2);

    
// float

    float f1 = 10;
    
float f2 = 10
;
    
bool r2 = Compare<float>
::IsEqual(f1, f2);

    
// double

    double d1 = 10;
    
double d2 = 10
;
    
bool r3 = Compare<double>
::IsEqual(d1, d2);

    
// pointer

    int* p1 = &i1;
    
int* p2 = &
i2;
    
bool r4 = Compare<int*>
::IsEqual(p1, p2);

    
// vector<T>

    vector<int> v1;
    v1.push_back(
1
);
    v1.push_back(
2
);

    vector
<int>
 v2;
    v2.push_back(
1
);
    v2.push_back(
2
);
    
bool r5 = Compare<vector<int> >
::IsEqual(v1, v2);

    
// custom template class 

    SpecializedType<float> s1 = {10.1f,10.2f};
    SpecializedType
<float> s2 = {10.3f,10.0f
};
    
bool r6 = Compare<SpecializedType<float> >
::IsEqual(s1, s2);