C++泛型编程-函数模板、类模板
模板是一种参数化多态性的工具,可以为逻辑功能相同而类型不同的程序提供代码共享的机制,为什么需要模板呢,这主要是因为下面这一原因?
强类型设计语言中,参与运算的所有对象的类型在编译的时候便可确定下来,但同时有一些副作用,比如下面的代码
int max(int a,int b)
{return a>b?a:b;}
//和
int max(float a,float b)
{return a>b?a:b;}
两种均为求最大值,但是参数类型不同
强类型的程序设计迫使程序员为逻辑结构相同而具体类型不同的对象编写模式一致的代码,但是无法抽取其中的共性,不利于程序的维护。
1.函数模板
1.1定义
定义格式如下:
template<模板参数表>
<返回值类型><函数名>(<参数表>)
{
<函数体>
}
其中:
关键字template是定义模板的关键字
<模板参数表>:包含一个或者多个用逗号分开的模板参数项,每一个有保留字class或者typename开始,后跟用户命名的标识符
1.2分类
模板函数主要分为普通模板函数和模板特化函数
代码如下:
#include "basic.h"
template<typename T1,typename T2>
void fun(T1 a, T2 b) {
cout << "普通模板函数" << endl;
}
template<>
void fun<int, char>(int a, char b) {
cout << "模板特化函数" << endl;
}
int main() {
//以下调用普通模板函数
fun(10, 10);
fun("hello", 10);
//下为模板特化函数
fun(10, 'c');
//显式指定模板参数
fun<int, int>(10, 'b');
return 0;
}
有以下三点需要注意:
1.模板特化优先级高:当参数类型完全匹配特化模板时,特化模板优先调用。
2.强制调用普通模板:如果希望调用普通模板,即使参数类型匹配特化模板,可以通过显式指定模板参数来实现:
3.避免歧义:如果模板重载和特化可能引发歧义,应尽量使用明确的调用方法或增加模板约束条件。
2.类模板
2.1定义
类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,因此类模板属于更高层次的抽象
格式如下:
template<模板参数表>
class<类模板名>
{
<类成员声明>
}
其中:
<模板参数表>包含一个或者多个用逗号分隔开的类型,参数项可以包含数据类型,也可以包含类属类型:如果是类属类型,需要加前缀class或者typename
注意:
类模板中的成员函数和重载的运算符必须为函数模板,它们的定义可以放在类模板的定义体中,与类中的成员函数的定义方法一致,也可以放在类模板的外部,则要采用以下的形式
template<模板参数表>
<返回值类型><类模板名><类型名表>::<函数名>(<参数表>)
{
<函数体>
}
2.2分类
对于类模板,主要有以下三类:全特化类 偏特化类 主版本模板类
代码如下:
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;
};
int main() {
Test<double, string> obj1(10.0, "hello");
Test<int, char> obj2(10, 'b');
Test<char, double> obj3('a', 10.99);
}
对主版本模板类、全特化类、偏特化类的调⽤优先级从⾼到低进⾏排序是:全特化类 > 偏特化类 > 主版本模板类