本文主要是介绍模板的特化和萃取,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
之前对模板化编程进行了总结(详见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的地址进行比较
}
函数模板的特化步骤:
- 必须先要定义一个函数模板
- 不能处理的类型进行特化
// 特化方式 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() ---> 以字符串返回类型名
这篇关于模板的特化和萃取的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!