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,如需转载请自行联系原作者
相关文章
|
4天前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。
|
4天前
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
4天前
|
C++
【C++】string类的使用④(常量成员Member constants)
C++ `std::string` 的 `find_first_of`, `find_last_of`, `find_first_not_of`, `find_last_not_of` 函数分别用于从不同方向查找目标字符或子串。它们都返回匹配位置,未找到则返回 `npos`。`substr` 用于提取子字符串,`compare` 则提供更灵活的字符串比较。`npos` 是一个表示最大值的常量,用于标记未找到匹配的情况。示例代码展示了这些函数的实际应用,如替换元音、分割路径、查找非字母字符等。
|
4天前
|
C++
C++】string类的使用③(修改器Modifiers)
这篇博客探讨了C++ STL中`string`类的修改器和非成员函数重载。文章介绍了`operator+=`用于在字符串末尾追加内容,并展示了不同重载形式。`append`函数提供了更多追加选项,包括子串、字符数组、单个字符等。`push_back`和`pop_back`分别用于在末尾添加和移除一个字符。`assign`用于替换字符串内容,而`insert`允许在任意位置插入字符串或字符。最后,`erase`函数用于删除字符串中的部分内容。每个函数都配以代码示例和说明。
|
4天前
|
安全 编译器 C++
【C++】string类的使用②(元素获取Element access)
```markdown 探索C++ `string`方法:`clear()`保持容量不变使字符串变空;`empty()`检查长度是否为0;C++11的`shrink_to_fit()`尝试减少容量。`operator[]`和`at()`安全访问元素,越界时`at()`抛异常。`back()`和`front()`分别访问首尾元素。了解这些,轻松操作字符串!💡 ```
|
4天前
|
存储 编译器 Linux
【C++】string类的使用②(容量接口Capacity )
这篇博客探讨了C++ STL中string的容量接口和元素访问方法。`size()`和`length()`函数等价,返回字符串的长度;`capacity()`提供已分配的字节数,可能大于长度;`max_size()`给出理论最大长度;`reserve()`预分配空间,不改变内容;`resize()`改变字符串长度,可指定填充字符。这些接口用于优化内存管理和适应字符串操作需求。
|
4天前
|
C++ 容器
【C++】string类的使用①(迭代器接口begin,end,rbegin和rend)
迭代器接口是获取容器元素指针的成员函数。`begin()`返回首元素的正向迭代器,`end()`返回末元素之后的位置。`rbegin()`和`rend()`提供反向迭代器,分别指向尾元素和首元素之前。C++11增加了const版本以供只读访问。示例代码展示了如何使用这些迭代器遍历字符串。
|
4天前
|
存储 编译器 C语言
【C++】string类的使用①(默认成员函数
本文介绍了C++ STL中的`string`类,它是用于方便地操作和管理字符串的类,替代了C语言中不便的字符数组操作。`string`基于`basic_string`模板,提供类似容器的接口,但针对字符串特性进行了优化。学习资源推荐[cplusplus.com](https://cplusplus.com/)。`string`类提供了多种构造函数,如无参构造、拷贝构造、字符填充构造等,以及析构函数和赋值运算符重载。示例代码展示了不同构造函数和赋值运算符的用法。
|
4天前
|
编译器 C语言 C++
【C++】模板初阶(下)
C++的函数模板实例化分为隐式和显式。隐式实例化由编译器根据实参推断类型,如`Add(a1, a2)`,但`Add(a1, d1)`因类型不一致而失败。显式实例化如`Add&lt;double&gt;(a1, d1)`则直接指定类型。模板函数不支持自动类型转换,优先调用非模板函数。类模板类似,用于创建处理多种数据类型的类,如`Vector&lt;T&gt;`。实例化类模板如`Vector&lt;int&gt;`和`Vector&lt;double&gt;`创建具体类型对象。模板使用时,函数模板定义可分头文件和实现文件,但类模板通常全部放头文件以避免链接错误。
|
4天前
|
机器学习/深度学习 算法 编译器
【C++】模板初阶(上)
**C++模板简介** 探索C++泛型编程,通过模板提升代码复用。模板作为泛型编程基础,允许编写类型无关的通用代码。以`Swap`函数为例,传统方式需为每种类型编写单独函数,如`Swap(int&)`、`Swap(double&)`等,造成代码冗余。函数模板解决此问题,如`template&lt;typename T&gt; void Swap(T&, T&)`,编译器根据实参类型推导生成特定函数,减少重复代码,增强可维护性。模板分函数模板和类模板,提供处理不同数据类型但逻辑相似的功能。