C++类模板的三种特化

简介:

 说起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);



本文转自莫水千流博客园博客,原文链接:http://www.cnblogs.com/zhoug2020/p/3176736.html,如需转载请自行联系原作者
相关文章
|
1天前
|
存储 编译器 C++
【C++】详解C++的模板
【C++】详解C++的模板
|
4天前
|
C++
【C++】日期类Date(详解)②
- `-=`通过复用`+=`实现,`Date operator-(int day)`则通过创建副本并调用`-=`。 - 前置`++`和后置`++`同样使用重载,类似地,前置`--`和后置`--`也复用了`+=`和`-=1`。 - 比较运算符重载如`&gt;`, `==`, `&lt;`, `&lt;=`, `!=`,通常只需实现两个,其他可通过复合逻辑得出。 - `Date`减`Date`返回天数,通过迭代较小日期直到与较大日期相等,记录步数和符号。 ``` 这是236个字符的摘要,符合240字符以内的要求,涵盖了日期类中运算符重载的主要实现。
|
1天前
|
编译器 C语言 C++
|
1天前
|
编译器 C++
【C++】详解初始化列表,隐式类型转化,类静态成员,友元
【C++】详解初始化列表,隐式类型转化,类静态成员,友元
|
2天前
|
安全 C++
详细解读c++异常模板复习
详细解读c++异常模板复习
|
4天前
|
存储 编译器 C++
【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值
C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。
|
4天前
|
编译器 C++
【C++】类和对象④(类的默认成员函数:取地址及const取地址重载 )
本文探讨了C++中类的成员函数,特别是取地址及const取地址操作符重载,通常无需重载,但展示了如何自定义以适应特定需求。接着讨论了构造函数的重要性,尤其是使用初始化列表来高效地初始化类的成员,包括对象成员、引用和const成员。初始化列表确保在对象创建时正确赋值,并遵循特定的执行顺序。
|
4天前
|
C语言 C++
【C++】日期类Date(详解)③
该文介绍了C++中直接相减法计算两个日期之间差值的方法,包括确定max和min、按年计算天数、日期矫正及计算差值。同时,文章讲解了const成员函数,用于不修改类成员的函数,并给出了`GetMonthDay`和`CheckDate`的const版本。此外,讨论了流插入和流提取的重载,需在类外部定义以符合内置类型输入输出习惯,并介绍了友元机制,允许非成员函数访问类的私有成员。全文旨在深化对运算符重载、const成员和流操作的理解。
|
4天前
|
定位技术 C语言 C++
C++】日期类Date(详解)①
这篇教程讲解了如何使用C++实现一个日期类`Date`,涵盖操作符重载、拷贝构造、赋值运算符及友元函数。类包含年、月、日私有成员,提供合法性检查、获取某月天数、日期加减运算、比较运算符等功能。示例代码包括`GetMonthDay`、`CheckDate`、构造函数、拷贝构造函数、赋值运算符和相关运算符重载的实现。
|
4天前
|
编译器 C++
【C++】类和对象③(类的默认成员函数:赋值运算符重载)
在C++中,运算符重载允许为用户定义的类型扩展运算符功能,但不能创建新运算符如`operator@`。重载的运算符必须至少有一个类类型参数,且不能改变内置类型运算符的含义。`.*::sizeof?`不可重载。赋值运算符`=`通常作为成员函数重载,确保封装性,如`Date`类的`operator==`。赋值运算符应返回引用并检查自我赋值。当未显式重载时,编译器提供默认实现,但这可能不足以处理资源管理。拷贝构造和赋值运算符在对象复制中有不同用途,需根据类需求定制实现。正确实现它们对避免数据错误和内存问题至关重要。接下来将探讨更多操作符重载和默认成员函数。