C++基础——函数与类模板
C++基础——函数模板与类模板
应该就是 C++ 的一个泛型编程了。
泛型编程
泛型编程主要是因为这样的一个应用场景:如果我要实现一个数的加法,那么数可以是整数,浮点数,长整数等等等,返回值也可以是这些类的其中一个,排列组合一下可以有很多种组合,但是它们函数内部的功能几乎一模一样,这样的话就会比较繁琐,泛型编程就很好的解决了这一个问题。
比如算竞常用的函数 sort 可以排序,它能排很多种数据类型,只要数据类型支持小于号,那么就可以使用 sort,就算没有小于号我们也可以重载小于号使用。
函数模板
1 |
|
我们先用一句 template<typename T>
去声明一个模板类型 T,然后再定义一个函数,这里我们让两个参数和返回值一致,使用模板类型,内部调用 + 返回即可,结果也是可以正常运行的。
其实这个模板只是一个模板,真正调用的时候会根据用到的情况去实例化一个具体的函数,比如这里我用到了 int float 和 string 类的 add 函数我就会在编译的时候根据模板生成这三个函数,分别对应了类型。
调试一下也可以看到
在整型调用的时候,call 的是 add<int>
函数,而浮点调用,call 的是 add<double>
函数,因此模板的作用是基于一个规则自适应地生成函数。
我们也可以显示地去调用指定类型的模板,比如:add<int>(1,2)
,在这里 <>
中的类型会替换里面所有的 T 类型实例化出一个具体的对象,如果对象不支持模板里的一些操作,那么它会在编译阶段报出错误。
以上的函数模板有一个缺点,就是说你的两个参数必须同一类型,也就是说调用 add(1,1.5)
会报错,那么为了解决这个问题,我们有以下的解决方案:
- 强制类型转换:这个应该都能想到,把它转成相同的就可以调用了。
- 显示指定类型:这个我们指定函数类型,若参数类型不符则在传参时候会自动强制转换。
- 模板重载:这个有点意思了,我们可以定义两个不同的 typename,这样的话即使两个类型不一样也可以支持了,但是前提是里面的操作需要支持我们调用时的类,
template<typename T1,typename T2>
。 - 非类型模板参数:我们可以在模板中定义参数,然后调用的时候在尖括号中传入。
1 |
|
运行之后可以发现没什么问题,函数正常调用并返回了正确的结果。
但是在模板列表中传参只能传 int 型的,类对象和浮点型不行。
类模板
这里就跟我们平时用到的 STL 库差不多了,声明方法和函数差不多,在类定义前声明一个模板类型,然后类内用模板类型的地方都会被替换成对应的显示类型,并且声明类的时候不支持隐式调用,因为它并不不能揣测你的内心想法。
demo
1 | template<typename T> |
我们实例化它也比较简单,跟STL差不多。
1 | A<int> s(1); |
模板全特化
有时候,我们希望某些类在被调调用的时候特殊处理,那么我们可以声明一个特化模板。
定义方法如下:
1 | template<> |
这样的话定义出来单独的类就会走新的调用空间了,我们上面调用 std::string 类的时候也会调用这里我们定义的函数。
模板偏特化
我们对主模板重定义
1 | template<typename T1,typename T2> |
然后呢为它再特化一个模板,特化条件是两个类型相同。
1 | template<typename T> |
然后分别调用一下来看看
1 | A<int,int> s(1); |
与函数一样,可以定义缺省,缺省其实就是默认参数,没传,它的值就是你给的默认值,你传了,用的就是你传的值。就像python里面的 int 函数,它有第二个参数表示是几进制的,你不传默认是十进制,你传了多少就是多少进制的,缺省参数必须在形参列表结尾。
demo
1 | template<typename T1,typename T2=int> |
与函数模板一样,里面也可以传 非类型模板参数,用法和函数模板一致,会基于参数生成不同的实例化类。