1.泛型编程

当我们要实现交换功能时,常规做法是编写参数类型固定的函数。比如写了一个接收int参数的交换函数,若要交换double类型数据,就不得不重新写一个参数为double的版本。

显然,为每一种数据类型都单独编写重复逻辑的交换函数既繁琐又冗余。而泛型编程的出现,正是为了解决这一问题 —— 它能让同一套函数逻辑适配多种数据类型,不需要重复编写了。

而泛型编程,虽然使用函数重载是可以完成的,但是重载的函数仅仅是类型不同,一旦出现新的类型,就需要我们自己手动添加对应的函数,代码的复用率很低。

那么能告诉编译器一个模子,让编译器根据不同类型利用模子来生成代码吗?

于是模版的概念就诞生了。

2.函数模版

模版分为函数模版与类模版。

2.1模版概念

函数模版代表了一个函数家族,该函数模版与类型无关,在使用时被参数化,根据实参类型产生的函数的特定类型版本。

并且模版能够实现动态增长和减小的数据结构,例如模拟动态数组能够根据实际存储数据进行自动扩容,以及一些数据结构栈堆之类的模拟。

2.2模版格式

template<typename T1,typename T2......typename Tn>

返回值类型 函数名(参数列表){}

代码展示如下:

template<class T>
void swap(T& a,T& b)
{
    T temp=b;
    b=a;
    a=temp;
}

根据这串代码,编译器就能通过实际使用的参数类型造出对应的函数。

其中class关键字在这里指的是类型参数而不是类,当然你也可以把关键字替换为typename这两种方式在绝大多数情况下是等价的。

3.模版原理

函数模版本身是一个蓝图,并不是函数,是编译器使用方式产生特定具体类型函数的模具,所以实际上模板就是把我们重复该做的交给编译器自动完成了。

4.模版实例化

用不同类型的参数使用函数模版时,被称为函数模板的实例化。

其中模版参数实例化又分为:隐式实例化,显式实例化。

1.隐式实例化:让编译器根据实参推演模版参数的实际类型

但也有一种情况。

template <class T>
int add(T& a,T& b)
{
    return a+b;
}

int main()
{
    int a1=10,a2=100;
    float b1=10.1,b2=100.1;
    ::add(a1,a2);
    ::add(b1,b2);
    ::add(a1,b1);
    return 0;
}

同类型之间的参数,编译器固然是能够根据实参类型来推到模版参数的实际类型。

但如果是不同类型的参数类型,根据上述代码,通过实参类型a1将T推导为int类型,根据b1将T推导为float类型,但是只有一个T,却有着两种推导可能无优先级从而无法判断,那么编译器就会报错。

毕竟单个模版参数要求必须是同一类型。

并且,在模板中编译器一般不会进行类型转换操作。

现在有两种方法1.自己强制类型转换,2.使用显式指定模版参数

int main()
{
    int a1=10,a2=100;
    float b1=10.1,b2=100.1;
    //显式指定模版参数
    ::add<int>(a1,b1);
    return 0;
}

如果类型不匹配,编译器就会尝试进行隐式类型转换,如果无法转换成功编译器就会报错。

另外最好要在模版前面加上域作用符,指明是在全局命名空间的函数,不然就比如swap这个函数,在库里面也是有的,你不加,就会产生调用歧义。

既然一个类型参数不能,那就多来一个,实现类型的均衡适配。

2.显式指定模版参数:在函数名后的<>中指定模版参数的实际类型

3.显式实例化

显式实例化是单独的声明语句。

如template int add<int>(int,int);与函数调用无关

5.模版参数匹配原则

一个非模版函数可以和一个同名的函数模版同时存在,而且该函数模版还可以被实例化为这个非模版参数。

就是说当同名的模版函数与非模板函数同时存在时,当参数类型个数完全匹配时,编译器就会优先调用非模版函数。

简称就是谁效率高就优先调用谁。

完全匹配优先,模版作为补充。

并且模版函数不允许自动类型转换,但普通函数可以进行自动类型转换。

6.类模版

类模板的定义格式

template<class T1,class T2...class Tn>
class 类模版名
{
//类内成员定义
};

模版不建议声明和定义分离到两个文件.h和.cpp会出现链接错误。

与类型无关,复用性很高。

并且模版类与平台无关,可以移植。

在编译阶段进行数据类型检查来确保数据类型安全,而不是在运行阶段。

6.1类模板实例化

类模板的实例化与函数模版的实例化不同,类模板的实例化需要在类模版名字后跟<>,然后将实例化的类型放在<>中即可,类模板的名字不是真正的类,只有经历过实例化的才是类对象。

更多推荐