c++模板特化和偏特化
模板的好处在于使得一个数据结构或算法的程序只需写一次,就能够使用各种具体数据类型(int,float.......等)。
这样写的模板固然有普遍适用性,但是由于针对各种数据类型都采取相同的处理方式,但是数据类型之间多多少少还是有一些区别,这样有的时候就不能兼容或者效率不是很高。
举个类模板不太兼容的经典例子:(比较大小)
//模板类
template<typename T>
class Test
{
static bool comp(T num1, T num2)
{
return (num1<num2)?true:false;
}
当我们传入的数据类型是int、double这些类型的时候还可以正常使用。
但是假如我们传入了一char类型,(我们都知道char类型比较大小必须用到strcmp函数,)那这时候这个类模板就无法兼容,就得需要专门特化一个char型的模板
//特化后的char型模板,传入的是字符串类型时,会优先调用特化后的模板(其实这个特化后的代码和原来关系不大,这里先不用纠结特化代码怎么写,下面会有详细的特化介绍,知道这回事就可以了)
template < >
class Test
{
static bool comp(T num1, T num2)
{
return strcmp(left, right); //和上一段代码的不同之处
}
引言:
偏特化和特化的不同之处在于,特化将所有的模板参数都固定下来,因而对类模板、函数模板特化的结果不再是模板,因为它不具有模板的可扩展性了,而是具有普通的类、普通的函数的性质。但是偏特化仍然保留了一部分的未定参数,仍然具有一定程度上的可扩展性,这使得偏特化后的结果仍然是模板。
全特化:
模板类的类型全部都已经明确化,全特化的类中的函数可以与模板类不一样。
偏特化包含两种:
1.模板中的模板参数没有被全部确定,需要编译器在编译时进行确定。
2.在类型上加上const、&、*( cosnt int、int&、int*、等等)并没有产生新的类型。只是类型被修饰了。
一:模板特化
注意:一个模板被称为全特化的条件:1.必须有一个主模板类(小tip:我们得将特化后的模板写在纯模板之后,否则会报错) 2.模板类型被全部明确化(区别于偏特化的部分明确化)
模板特化的固定格式: 前面有一个template<> 然后下面就是特化后的函数代码,(其实这样看来模板特化和新写一个函数(类)其实差别不大)
调用顺序:特化>偏特化>纯模板
1.函数模板特化
示例:(下面的例子来自于网上)
//模板函数,未经特化
template <class T>
int compare(const T &left, const T&right)
{
return (left - right);
}
//特化后的函数(显而易见的就看出来特化后的代码和原来的有很大的不同)
template < > //虽然这里面啥也不写,但是仍然得写上,这就是提示编译器和程序员的一个标志
int compare<const char*>(const char* left, const char* right)
{
return strcmp(left, right);
}
2.类模板特化
程序例子就不举了,文章开头那个例子就是。
几个注意点:
来自于:https://www.cnblogs.com/qlee/archive/2011/06/27/2091523.html
* 如果类模板中含有静态成员,那么用来实例化的每种类型,都会实例化这些静态成员。
* 两个靠在一起的模板尖括号( > ) 之间需要留个空格,否则,编译器将会认为是在使用operator>>,导致语法错误。
* 特化的实现可以和基本类模板的实现完全不同。
* 类模板可以为模板参数定义缺省值,称为缺省模板实参,并且他们还可以引用之前的模板参数。
* 成员函数模板不能被声明为虚函数。
二:模板偏特化
主要是类模板的偏特化,函数模板的偏特化没必要,因为可以通过重载来完成。
偏特化的条件:1.必须有一个主模板 2.模板类型被部分明确化
这里的语法形式和特化时的有明显的差异,由于类模板经过偏特化后仍然是模板,所以依然要以template<参数类型表>来开头
1.类模板的偏特化
//纯模板,未偏特化
template<class T1, class T2>
class A
{
..............
}
//偏特化后的模板,可以看出T2被我们明确成为了int类型。
template<class T1>
class A<T1, int> //区别之处
{
............
}
2.函数模板的重载
其实,对于函数而言,虽然不能偏特化,即不能再在函数名字后面像模板类一样搞个<typename T>出来,但是可以通过函数的重载(注意这里说的重载是指的模板重载,而不是普通意义的函数重载)来实现偏特化。
程序示例:
//原模版
template <typename T1, typename T2>
bool test(T1 t1, T2 t2)
{
return t1 < t2 ? t2 : t1;
}
//第一种偏特化
template <typename T1>
bool test(T1 t1, int t2)
{
return t1 < t2 ? t2 : t1;
}
//第二种偏特化,偏特化成为一个指针或者引用类型
template<class T>
T *test(T *t1,T* t2)
{
return (*t1>*t2)?t1:t2;
}
总结:
1.
但是模板特化并不只是为了性能优化,更多是为了让模板函数能够正常工作,最典型的例子就是STL中的iterator_traits。algorithm中大多数算法通过iterator对象来处理数据,但是同时允许以指针代替iterator对象,这是为了支持C-Style Array。如果直接操作iterator,那么为了支持指针类型,每个函数都需要进行重载,因为指针没有::value_type类型。为了解决这个问题,STL使用了iterator_traits,并为指针类型进行转化,算法通过它来操作iterator,不需要知道实际操作的是iterator对象还是指针。
-
template<typename IteratorClass> class iterator_traits
-
...
-
template<typename ValueType> class iterator_traits<ValueType*>
-
...
-
template<typename ValueType> class iterator_traits<ValueType const*>
-
...
后面两是针对指针类型的偏特化,也是偏特化的一种常见形式。
2.
非类型模板参数:
在编译期或链接期可以确定的常值。这种参数的类型必须是下面的一种:
a> 整型或枚举 例子:template<class T,int size>
b> 指针类型( 普通对象的指针,函数指针,成员指针 )
c> 引用类型( 指向对象或者指向函数的引用 )
其他的类型目前都不允许作为非类型模板参数使用
3.
偏特化(半特化)的类型可以是我们自定义的类型
// 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);
}
};
上一篇: Linux上手动安装MySQL
下一篇: 模板的特化和偏特化以及相关理解