模板的特化和萃取

之前对模板化编程进行了总结(详见https://blog.csdn.net/timecur/article/details/89949643)。这篇将介绍模板的重要概念---模板特化。

模板的特化

        模板针对某些具体的类型不能处理或者处理结果有误,就需要进行模板特化。

// 例如一个比较两元素大小的模板
template<class T>
T& Max(T& left, T& right){
    return left > right ? left : right;
}

// 模板特化
template<>  
char*& Max<char*>(char*& left, char*& right){
    if(strcmp(left, right) > 0)
        return left;
    return right;
}

int main(){
    char* p1 = "world";
    char* p2 = "hello";
    std::cout << Max(p1, p2) << std::endl;  // 输出结果永远是p1的值, 因为它是将p1与p2的地址进行比较
}

函数模板的特化步骤:

  1. 必须先要定义一个函数模板
  2. 不能处理的类型进行特化
// 特化方式
template<>
返回值类型 FuncName<特化类型>(参数列表)
{}

注意: 特化版本必须与模板函数的原型要一致;

            特化比较麻烦,如果特化不了,可以直接将该类型的函数直接给出。

  • 全特化

        全特化:将模板参数类表中所有的参数都确定化。

   样例代码:

template<class T1, class T2>
class Data{
public: 
    Data() {
        cout<<"Data<T1, T2>" <<endl;
    }
private: 
    T1 _d1; 
    T2 _d2;
};

template<>
class Data<int, char>{
public:
    Data() {
       cout<<"Data<int, char>" <<endl;
 }
private: 
    T1 _d1; 
    T2 _d2;
};
void TestVector(){ 
    Data<int, int> d1; 
    Data<int, char> d2;
}

 

  • 偏特化

        偏特化:将模板参数列表的部分参数特化

   样例代码:

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>{
public:
    Data() {
        cout<<"Data<T1, int>" <<endl;
    }
private:
    T1 _d1;
   int _d2;
}; 

        使模板参数限制更加严格。

   样例代码:

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>{ 
public:
    Data() {
        cout<<"Data<T1*, T2*>" <<endl;
    }
 
private:
   T1 _d1;
   T2 _d2;
}

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
 Data(const T1& d1, const T2& d2)
     : _d1(d1)
     , _d2(d2)
 {
     cout<<"Data<T1&, T2&>" <<endl;
 }
 
private:
     const T1 & _d1;
     const T2 & _d2; 
 };
void test2 () 
{
     Data<double , int> d1; // 调用特化的int版本
     Data<int , double> d2; // 调用基础的模板 
     Data<int *, int*> d3; // 调用特化的指针版本
     Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

        可以看出指针或是引用类型的特化更多的是对类型做了某些限定,但仍保留了一定的模板性,但这种特化提供了极大的方便。

        注: 严格来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。

 

  • 类型萃取

       类型萃取可以说是模板特化的应用衍生出的概念。具体来说就是萃取某一特定类型,将该类型进行特殊的处理,以此来提高运行效率。

      使用类型萃取能提供一种根据类型的某些属性在编译时期进行函数派送的机制,减少代码混乱。

    代码演示说明(例实现通用拷贝函数):

  • 有上述概念可知,我们需要对特定类型进行萃取,从而进行特殊处理。数据类型可分为内置类型和自定义类型,那好我们就对这两种类型进行类型萃取。

        首先定义两种类型:

 struct TrueType {};  // 内置类型
 struct FalseType{};  // 自定义类型
  • 特化需要处理的类型。
template <class T>
struct TypeTraits
{
	typedef __FalseType IsPodType;
};
 
// 对字符型进行特化
template <>
struct TypeTraits <char>
{
	typedef __TrueType IsPodType;
};

// 其他内置类型特化方式类似不再赘述 ...
  • 通过函数派送机制,避免使用if/else语句来判断是内置类型或自定义类型。再通过不同的类型调用特定的方法。
template <class T>
inline T* TypeCopy(const T* src, T* dst, size_t n)
{
	return __TypeCopy(src, dst, n, TypeTraits<T>::IsPodType());
}
  • 通过重载实现不同类型的不同拷贝方法。

       内置类型可以通过memcpy方式进行拷贝。而自定义类型可能涉及资源的管理,所以用memcpy会出现浅拷贝问题,所以在这里使用深拷贝的方式处理。

template <class T>
inline T* __TypeCopy(const T* src, T* dst, size_t n, __TrueType)		
{
	cout << "内置类型拷贝:" << typeid(T).name() << endl;
	return (T*)memcpy(dst, src, sizeof(T) * n);
}
 
template <class T>
inline T* __TypeCopy(const T* src, T* dst, size_t n, __FalseType)	
{
	cout << "自定义类型拷贝:" << typeid(T).name()<< endl;
	for (size_t i = 0; i < n; i++)
	{
		dst[i] = src[i];
	}
	return dst;
} 

        typeid(Type).name() ---> 以字符串返回类型名

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值