泛型编程
首先,如果我们要探讨c++中模版相关的问题,自然绕不开c++所支持的牛逼特性(泛型编程)。什么是泛型编程?当我们在写python的时候,我们注意到python和c++很大的一个区别就是,python不用定义一个变量的类型;我们在python中写一个函数时,不用太早决定参数的类型,这个好处使得一个python的函数可以处理无数的输入类型。
然而c++可就不方便了,由于需要指定参数类型,c++必须对每一种类型的输入单独写一个函数,即使这些函数都是执行相同的操作:
def add(a) :
return a + 1
int add(int a) {
return a + 1;
}
float add(float a) {
return a + 1;
}
double add(double a) {
return a + 1;
}
如上所示,我们要实现一个对于int float double通用的+1函数,python只需要一个,而c++需要三个;由于函数体完全相同,这无疑增加了很多无用工作与无用代码。
什么是模版
为了解决这个问题,就需要模版出场,c++代码只要写成下面这样,就可以避免重复工作了~
template<typename T>
T add(T a) {
T b = a + 1
return b;
}
add<int>(a);
add<float>(a);
add<double>(a);
其中的T,所指的就是add函数支持的一切类型,我们把这些类型都抽象记为T;在实际工程中,几乎所有函数都是模版化的,所以在调用add函数时会这么写:
add<Ta>(a); //其中Ta是调用add函数的函数中的模版类型参数(和add函数中的T一样),这样无论什么类型的输入,都只需要一行代码就可以解决了~
什么是模版特化
如果这个add函数只需要处理int float double三种类型,那么现有的设计就足够了,但是假设这个函数对于字符串也要处理,而且处理的方式是往输入的后面拼接一个‘1’,这样的话就需要针对输入为字符串的情况单独写一个add函数。这个时候我们首先想到的是函数重载,只要这么写一下不就ok了吗?
string add(string a) {
string b = a + '1';
return b;
}
的确这么写利用重载可以解决输入为 string时的问题,但是这会导致我们调用add时的格式不统一!我们不能再通过add<Ta>(a);来解决输入为string时的情况,而是要写成add(a);。
这样就导致我们前期使用模版的努力前功尽弃了,那么有没有办法既保持调用add的形式不变,又能够特殊处理输入string的情况呢?
模版特化正式登场:
template<>
string add(string a) {
string b = a + '1';
return b;
}
template<typename T>
T add(int a, T c) {
T b = a + c;
return b;
}
上面第一种写法称之为模版的全特化,因为它具体化了函数中的全部数据类型,template中再也没有抽象的typename T,只剩下空荡荡的<>。与此相对的是模版的偏特化,它只具体化了add的部分参数类型,第二个类型还是泛型。
通过模版特化,我们成功地在保持调用形式不变的情况下,对于函数的特殊情况进行了特殊处理,这就是特化相比较于重载的好处~