C++模版进阶

本文详细探讨了C++中的模板技术,包括函数模板的特化处理(如针对指针的比较)、类模板的全特化和偏特化,以及非类型模板参数的使用限制。还介绍了模板的分离编译策略,以提高程序的组织和编译效率。
摘要由CSDN通过智能技术生成

       

目录

一、非类型模板参数

二、函数模板的特化

(1)函数模板特化的引入

(2)函数模板特化的步骤

三、类模板的特化

        (1)全特化

(2)偏特化

1.部分特化:将参数模板中的一部分类型限定死

2.参数限制特化:偏特化不一定指的是要某一种特定的类型,也可以是限定指针等类型

四、类模板特化的应用示例

五、模板的分离编译


         在之前的文章中,我们已经简单的讲解了模版的使用,但是这些都是最为简单初学的语法,本篇文章中,我们会详细的讨论c++真正的模板。

一、非类型模板参数

        模板参数分为类类型模板参数和非类型模板参数

类型形参:出现在模板参数列表中,跟在class或者typename后面的那个T,通常需要我们传入一个类型编译器才能实例化。

非类型形参:就是用一个常量(只能是整型)作为类(函数)模板的一个模板参数,在模板中将其当做一个常量来使用,就好像我们普通函数的形参

注意:

1.浮点数、类对象、以及字符串是不允许用作非类型模板参数的,只有整型家族的才能用作非类型模板参数

2.非类型模板参数必须在编译器就能确定结果,也就是在编译的时候就要知道其具体的值

二、函数模板的特化

(1)函数模板特化的引入

        通常情况下,使用模板可能产生一些与类型无关的代码,甚至还有可能出现错误,所以需要我们的特殊处理,比如有一种使用场景需要我们比较指针所指向的内容,但是我们传入的是一个地址(因为对于太大的对象,传对象会导致效率低下,所以一般会传引用或者指针)

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}
int main()
{
cout << Less(1, 2) << endl; // 可以比较,结果正确
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; // 可以比较,结果错误
return 0;
}

        可以看到,在大多数情况下,less都是可以比较出大小的,但是在特殊使用场景下就会导致错误。上述示例中:由于less比较的是指针的地址了,导致了最后一组数据出现了问题。但是普通情况下又是正确的,所以我们需要使用模板的特化,在原模板的基础上,针对该特殊情况增加一种事项方式

(2)函数模板特化的步骤

函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
return *left < *right;
}
int main()
{
cout << Less(1, 2) << endl;
Date d1(2022, 7, 7);
Date d2(2022, 7, 8);
cout << Less(d1, d2) << endl;
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
return 0;
}

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出

像这样写简单明了,代码可读性高,容易书写。因为对于一些参数类型复杂的函数模版,特化时容易出错,因此函数模板我们并不推荐特化(类模板可以特化)

三、类模板的特化

        特化的目的就是在我们传入不同的模板参数的时候,能够让编译器有不同的选择,走到最合适的路中

        (1)全特化

        全特化是将模板参数列表中的所有参数都确定化

(2)偏特化

        偏特化是 任何针对参数模板进一步进行条件限制的特化版本。

比如针对于以下模板类:

偏特化有以下两种表现方式:

1.部分特化:将参数模板中的一部分类型限定死

2.参数限制特化:偏特化不一定指的是要某一种特定的类型,也可以是限定指针等类型

两个参数偏特化为指针类型:

两个参数偏特化为引用类型:

四、类模板特化的应用示例

假设有如下专门用来按照小于比较的类模板Less:

下面是在Priority_Queue中的一端代码,我们可以看看并理解模板的妙用

这里的逻辑是:在创建优先级队列的时候,就已经传入了Compare的类型,即Less<T>,然后创建了一个com对象,这个com对象有且仅有一个公有函数operator(),他的两个参数都是T类型(在创建对象的时候就已经决定了)所以可以比较

五、模板的分离编译

        一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链 接起来形成单一的可执行文件的过程称为分离编译模式。

        假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

        可以看到这里模板的声明和定义是分开到.h文件和.cpp文件中的,如果直接运行则会报错,这是为什么呢?

分析:

        

我们的解决办法是:

1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。

2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

  • 17
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值