C++——模板与特化

写在前面

关于模板之前就有所了解,但是一直没有系统整理,而且对特化印象不深,所以借此机会,系统的学习一下模板与特化的相关知识,还望大家多多批评指教。

参考文献:

模板的概念

模板,Template,是一种将数据类型参化的工具,提供了一种将代码与数据类相脱离的机制,即代码不受具体的数据的类型的影响,从而为C++提供了泛型编程的方式,减少冗余代码的同时依然可以提供类型安全。模板分为两种:

  • 类模板
  • 函数模板

特化的概念

特化,Specialization,是针对某个特定的类型,在模板定义时给出不同一般数据类型的逻辑实现。然而在使用的时候,这个特殊性完全被屏蔽,我们仍然只需要按照模板来使用,编译器会根据模板的代码设定,针对特别的数据类型调用我们设定的特别的代码逻辑。

简单点来说,已有的模板的形参类型不受任何的限制,什么参数类型都可以,但是考虑到实际情况中,可能存在某些特定的数据类型不满足当前的泛型模板的代码逻辑,就需要针对特定的数据类型,对模板进行新的代码逻辑的设计,就是特化。

特化的分类

针对特化的对象不同,可以分为两类:

  • 类模板的特化:当类模板内需要对某些类型进行特别处理时,使用类模板的特化。
  • 函数模板的特化:当函数模板需要对某些类型进行特别处理时,使用函数模板的特化。

针对特化的参数个数,可以分为两类:

  • 全特化:对模板中所有的模板参数指定为确定的类型,全特化可以看做定义了一个全新的类型,全特化中的类的成员函数可以与模板类的成员函数不一样。
  • 偏特化:对模板中一部分的模板参数指定为确定类型,另一部分没有被确定类型,需要在编译器进行编译时确定。

注意:

  • 严格来说,函数模板只能全特化,函数模板并不支持偏特化。可以通过函数的重载,来达到类似偏特化的效果。
  • 类模板支持全特化与偏特化。

模板的声明

类模板与函数模板的声明方式是一样的,在类或函数定义之前声明模板参数列表,示例代码如下:

// 类模板
template <class T1, class T2>
class A{
    T1 data1;
    T2 data2;
};

// 函数模板
template <class T1, class T2>
T1 Max(const T1 lhs, const T2 rhs){   
    return lhs > rhs ? lhs : (T1)rhs;
}

模板的全特化

通过全特化一个模板,可以对一个特定参数集合,自定义当前的模板。类模板与函数模板都可以全特化。全特化的模板参数列表是空的:<>,并应给出“模板实参”列表。示例代码如下:

// 全特化类模板
template <>                 //告诉编译器这是一个特化的模板。
class A<int, double>{
    int data1;
    double data2;
};

// 函数模板
template <>                 //告诉编译器这是一个特化的模板。
int Max(const int lhs, const double rhs){   
    return lhs > rhs ? lhs : (int)rhs;
}

注意:

这里对于函数模板,由于上述代码中,Max是有形参列表的,所以编译器是可以通过函数签名:int max(const int, const int),来推导出Max是函数模板:T1 Max(const T1, const T2),的特化。但如果函数没有形参列表呢?这时如果让编译器自动推导,使用隐式实例化的方法,就会报错。举个例子:

template <class T>
void f(){ T d; }

template <>
void f(){ int d; }

这时,编译器会报错:

error: no function template matches function template specialization 'f'

所以,为了解决这种问题,我们需要显示实例化,或者说显示特化函数模板:

template <class T>
void f(){ T d; }

template <>
void f<int>(){ int d; }

模板的偏特化

类似于全特化,偏特化也是为了给自定义一个参数集合的模板,但偏特化后的模板需要进一步的实例化才能形成确定的签名。 值得注意的是函数模板不允许偏特化,这一点在Effective C++: Item 25中有更详细的讨论。 偏特化也是以template来声明的,需要给出剩余的”模板形参”和必要的”模板实参”。示例代码如下:

template <class T2>
class A<int, T2>{
    ...
};

函数模板不支持偏特化,但是可以通过重载实现类似的需要:

template <class T1, class T2>
void f(){}

//Error
template <class T2>
void f<int, T2>(){}

//Right
template <class T2>     // 注意:这里模板声明不一样
void f(){}              // 注意:这里没有"模板实参"

模板的其他类型特化

之前介绍的特化类型,仔细观察就不难发现,主要是特化为绝对类型。但其实还可以将类型特化为指针或引用类型,或者特化为另一个类模板。

特化为引用、指针类型

参考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特化为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);
    }
};

特化为另一个类模板

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

// 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还是vector, 我们可以不关心, 因为对于这两种类型,我们的处理方式是一样的,我们可以把这种方式称为“半特化”。

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

// 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);
    }
};

猜你喜欢

转载自blog.csdn.net/zy2317878/article/details/80679512