欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

c++模板特化和偏特化

程序员文章站 2024-01-14 17:27:58
...

模板的好处在于使得一个数据结构或算法的程序只需写一次,就能够使用各种具体数据类型(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 来自:https://blog.csdn.net/m_buddy/article/details/72973207

    1.

    但是模板特化并不只是为了性能优化,更多是为了让模板函数能够正常工作,最典型的例子就是STL中的iterator_traits。algorithm中大多数算法通过iterator对象来处理数据,但是同时允许以指针代替iterator对象,这是为了支持C-Style Array。如果直接操作iterator,那么为了支持指针类型,每个函数都需要进行重载,因为指针没有::value_type类型。为了解决这个问题,STL使用了iterator_traits,并为指针类型进行转化,算法通过它来操作iterator,不需要知道实际操作的是iterator对象还是指针。

 

  1. template<typename IteratorClass> class iterator_traits

  2. ...

  3. template<typename ValueType> class iterator_traits<ValueType*>

  4. ...

  5. template<typename ValueType> class iterator_traits<ValueType const*>

  6. ...

后面两是针对指针类型的偏特化,也是偏特化的一种常见形式。

 

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

 

相关标签: c++