模板全特化与偏特化的概念
前言
之前我在学习STL的时候,发现STL用到了大量的类模板、函数模板。对于模板而言,我们知道,当用户传递类型后,模板会进行自动类型推演,但是作为一个模板初学者,我有时候并不能确定模板一定会按照我的思路去推导出符合我预期的类型,所以有时候编写的模板能够涵盖的类型过多反而也不是一件好事(如果代码出现错误,很难察觉到),那么为了让编译器更加精准了解我们想要的类型,我们就可以对模板进行特化(特例化),让编译器更加精确限定我们需要的模板类型,让模板的泛型不那么“泛”。
本文主要介绍模板的特化(完全特化及偏特化)在类和函数中是什么样的情况。说白了我其实是想梳理一下C++绑定器和C++11的function机制,为此才写本篇文章,做一些前置的知识铺垫。
关于为什么要用模板以及模板的好处在哪?模板实际上就是宏定义的衍生,相比宏,其还可以进行类型检查,更安全,这里就不再过多赘述,可参考:[🔗模板特化的概念从何而来]
模板特化
模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。
模板分为类模板与函数模板,特化分为全特化与偏特化。全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。
类模板(全特化、偏特化)
类模板的类型传递通过<>
传递,那么类模板配合特化使用后,产生的全特化和偏特化可以看下面的代码,全特化指的是我要对模板传递的所有类型进行明确的指定,即下面代码的第二部分,同时制定了T1为int,T2为char;偏特化指的是只对模板的部分类型进行明确指定,即下面代码的第三部分,指定第一个参数为char,第二个参数依旧保持T2,让模板自动推导。
// 类模板
template<typename T1, typename T2>
class Test {
public:
Test(T1 i, T2 j) : a(i), b(j) { cout << "模板类" << endl; }
private:
T1 a;
T2 b;
};
// 类模板全特化
template<>
class Test<int, char> {
public:
Test(int i, char j) : a(i), b(j) { cout << "全特化" << endl; }
private:
int a;
char b;
};
// 类模板偏特化
template <typename T2>
class Test<char, T2> {
public:
Test(char i, T2 j) : a(i), b(j) { cout << "偏特化" << endl; }
private:
char a;
T2 b;
};
那么下面3句依次调用类模板、全特化与偏特化:
Test<double, double> t1(0.1, 0.2); // 类模板
Test<int, char> t2(1, 'A'); // 模板全特化
Test<char, bool> t3('A', true); // 模板偏特化
函数模板(只有全特化)
而对于函数模板,却只有全特化,不能偏特化:
//模板函数
template<typename T1, typename T2>
void fun(T1 a , T2 b) {
cout << "模板函数" << endl;
}
//全特化
template<>
void fun<int, char >(int a, char b) {
cout << "全特化" << endl;
}
//函数不存在偏特化:下面的代码是错误的
/*
template<typename T2>
void fun<char, T2>(char a, T2 b) {
cout << "偏特化" << endl;
}
*/
为什么函数不能偏特化呢?似乎不是因为语言实现不了,而是因为偏特化的功能可以通过函数的重载完成。
总结
以上就是对类模板特化和函数模板特化使用方式的介绍,对模板特化有了一个概念上的认识。那么全特化其实也就是一个“披着空空如也的template<>
的普通函数或类”。其实模板的使用场景一定是非常复杂的,我所接触的模板场景也并不多,本篇主要是对模板特化概念层面的总结,哎,C++语法可真多,你问我为什么要学C++,纯纯是靠情怀硬撑着(因为可以装逼)。