查看: 86|回复: 1

萌新的C++笔记: 泛型编程之类模板

[复制链接]

2

主题

8

帖子

16

积分

新手上路

Rank: 1

积分
16
发表于 2023-1-14 13:19:38 | 显示全部楼层 |阅读模式
常规类模板:

类模板与函数模板很相似,通常情况,程序仅在需要时,按照类模板生成独立的类声明和类方法。生成的可执行程序中不包含类模板。其使用方式如下:


且在定义类成员函数时,也需带上template关键字。在作用域解析运算符左边的类名也必须带上<T>,表示一个类型。比如,T == int ,那么Stack<T>就变成了Stack<int>,这表明,Stack<int>是一个类型。如果T==float,那么Stack<T>就变成了Stack<float>,这也同样表明,Stack<float>又是一个类型。也就是说,类名+类形参,才是一个完整的类型:


与函数模板一样,T表示类型参数,类似于形参,但只能将类型赋给它。但与函数模板不同的是,在实际使用时,必须显示提供所需的类型。因为编译器可以根据函数参数类型来自动进行推导,但无法在实例化类对象时对类型进行推导:


此时的Stack<int>表示int版本的Stack,它是一个单独的类型。Stack<float>表示float版本的Stack,它也是一个单独的类型。

多个类型参数

模板可以使用多个类型参数:



默认类型模板参数

类模板可以为类型参数提供默认值(函数模板不能为类型参数提供默认值,但是可以为非类型参数提供默认值)




对于ta来说,类型是TestA<float, char>。对于tb来说,因为省略了第二个类型参数,而第二个类型参数的默认值是int,所以tb的类型是TestA<float, int>。

非类型参数(表达式参数):

在模板参数中,还可以使用非类型参数。




编译器在定义该类时,将使用double替换T, 6替换n。
但表达式参数有一些限制。表达式参数可以是整型、枚举、引用、指针。但不能是别的类型,比如double。这里使用double就是不合法的。



类模板的隐式实例化

正如函数模板的隐式实例化是在函数调用时发生的那样,类模板的隐式实例化是声明对象时发生的。此时,编译器根据提供的类型参数和类模板生成具体的类定义。


但,如果只是声明类型的指针,而没有实际的生成类对象,那么隐式实例化也不会发生,如图:


第一句不会发生隐式实例化,因为并没有实际生成类对象。第二句发生了隐式实例化。

类模板的显示实例化

如同函数模板的显示实例化一样,类模板也有显示实例化。无论有没有声明对象,编译器在遇到这个语句后,都将按照模板和语句声明的类型来生成实际的类定义:



类模板的显示具体化(特化)

与函数模板类似,类模板的显示具体化也是特定类型的定义。有时候,可能需要在为特殊的类型实例化时,对模板进行修改,使其拥有不同的行为。




在编译器需要为TestA<char>类型进行定义时,会使用专门的显示具体化版本,而不是常规的通用模板定义。

类模板的部分具体化(偏特化)

与函数模板不同,函数模板没有部分具体化。类模板的部分具体化指,让部分类型参数具体化为某种类型,不再表示泛型参数。


上图的例子,将类型参数T2具体化为float,而T1依然表示泛型。只有部分类型参数被具体化,所以叫部分具体化。template<>中必须至少有一个泛型参数,里面的参数表示的是没有被具体化的类型参数。如果template<>中的类型参数为空,那就表示显示具体化。
甚至也可以将T2的类型具体化为T1:



成员模板

模板可用作结构、类或模板类的成员。在结构、类或类模板中定义另一个模板:
下图,在名为Alpha的类模板中又定义了一个成员类模板,如图中黄色方框部分。因为其访问权限是私有的,所以只能在Alpha类中访问它。
红色方框部分的是一个类成员函数模板。其类型参数U在函数调用时根据传入的参数来推导,而类型参数T在Alpha类实例化时确定。


当然,也可以在类模板中,先声明,再到类模板外定义。如图:




最外面的一层template<typename T>表示成员模板所属的类模板。第二次的template<typename V>和template<typename U>才表示成员模板自己。正如之前所说,类模板的类型参数要有实际的类型,这个类模板才能表示一个类型。所以,类名Alpha右边必须要带有<T>,Alpha<T>才是一个完整的类型。

将模板用作参数

既然是泛型,将类型用做参数,那么,也能把模板用作参数:


将模板用做参数时,只需将原来的typename替换成类模板的声明即可。

模板类和友元

非模板友元:

在模板类中将一个常规函数声明为友元,此友元函数是一个常规函数,并非模板函数,所以叫非模板友元。


该声明使Test()函数成为类模板所有实例化的友元。比如,它将是类Alpha<int>、类Alpha<float>、类Alpha<char>等的友元。

假如要为函数提供模板类型作为参数,可以这样声明:


但是,与Test()不同的是,report()函数不是所有Alpha类模板实例化的友元。比如,report(Alpha<int>&)是Alpha<int>的友元,report(Alpha<double>&)是Alpha<double>的友元。
在定义report()函数时,必须为要使用的友元定义显示具体化:



约束模板友元:

友元的类型取决于类被实例化时的类型。此时,友元函数变成了一个模板函数。所谓的约束,是指,将类模板的每一个具体化与一个具体化的模板友元匹配。类模板的每一个具体化都只与它匹配的模板友元是友元。
其使用方式如下:
1.在类定义的前面声明每个模板函数:


2.在函数中再次将模板声明为友元:
与非模板友元类似,带了类型参数之后,模板友元便不再是所有类模板实例化的友元了。而是某个具体化的类模板的友元。比如,Alpha<int>的友元是void Test<int>()和void report<Alpha<int>>(Alpha<int>& t)。
Alpha<double>的友元是void Test<double>()和void report<Alpha<double>>(Alpha<double>& t)


3.为友元提供定义:


在这里可以看到,和之前的非模板友元比较。模板友元就无需为多种类型单独显示具体化函数定义了。

非约束模板友元:

友元的所有具体化,都是类的每一个具体化的友元。意思是,模板友元不论被具体化为什么类型的函数,对于该类模板的每一个具体化来说,这个函数都是友元。它并非是某个类型与某个类型一一匹配的。这是与约束模板友元的一个区别。这里的具体化并非指显示具体化,而是指,模板获得了实际的类型参数后,所表示的类型。
非约束模板友元与约束模板友元的另一个不同是,非约束模板友元在类模板中声明:



模板别名:

这是c++11新增的一项功能,可以通过使用模板别名来简化使用:


类型Beta<float>相当于类型Alpha<float, 6>。
当然,using 也可以单独使用:


上图,给char指针常量取别名为 char_cp, 给指向int指针的二级指针取别名为 int_p2。
回复

使用道具 举报

1

主题

3

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2025-3-15 15:49:42 | 显示全部楼层
沙发位出租,有意请联系电话:13838384381
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表