C++_模板的特化

       

目录

一、非类型模板参数

二、模板的特化 

1、特化的概念

2、函数模板的特化

3、类模板的特化

3.1 偏特化

3.2 全特化

3.3 部分特化

结语:


前言:

        大多数情况下模板可以解决许多重复性工作,因为把不同的类型传给模板参数后,编译器就会自动生成该类型的函数或类,但是如果传给模板参数的类型是特殊类型(比如:指针类型),那么最终的结果可能会有所偏差,原因就是该模板的内容实现逻辑是按照普通类型的实现逻辑进行的。这时候如果传指针类型给到该模板,则需要先对指针进行解引用后才能走普通类型的实现逻辑,如果直接对指针本身进行操作则结果会发生错误

一、非类型模板参数

        在介绍模板的特化前,先浅浅的介绍一些非类型模板参数。模板参数列表中除了可以把类型作为参数,还能将常量作为参数。

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T,size_t n>//n是一个常量
class arr
{
public:arr(size_t y = n)//缺省值为n{for (size_t i = 0; i < n; i++){_arr[i] = y;}}T& operator[](size_t i){return _arr[i];}private:T _arr[n];//表示_arr数组的大小是模板参数n
};int main()
{arr<int,10> a;for (size_t i = 0; i < 10; i++){cout << a[i] << " ";}cout << endl;return 0;
}

        运行结果:

        注意事项:

        1、非类型模板参数的类型只能是整形家族的(即:不能是浮点型、自定义类型...等等)。

        2、非类型模板参数是常量,不可被修改。 

二、模板的特化 

1、特化的概念

        特化就是特殊处理的意思,因为模板在处理指针类型的时候,由于处理逻辑的偏差,导致结果会发生错误,因此需要再写一个模板专门针对指针类型的处理,这个模板就叫做特化。(特化的模板是根据主模板而写的,因此要特化模板的前提是必须有主模板

        体现特化作用的具体示意图如下:

        因为模板分为函数模板和类模板,因此模板的特化分为:函数模板特化和类模板特化。 

2、函数模板的特化

        体现特化作用的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T>//函数模板
bool Less(T left, T right)
{return left < right;
}int main()
{int a = 10;//先创建,a处于高地址int b = 20;//后创建,b的地址量级比a低cout << Less(a, b) << endl; // 可以比较,结果正确int* pa = &a;int* pb = &b;cout << Less(pa, pb) << endl; // 可以比较,但比较的是地址数,结果错误return 0;
}

        运行结果:

        从结果可以看到,pa和pb进行比较的时候,我们期望的其实是pa指向的空间和pb指向的空间的比较,而不是pa和pb本身值的比较,因此可以写一个专门处理指针类型pa和pb的模板特化。(当然,这里重新写一个模板处理指针类型也可以,这里只是示范一下模板特化的写法


       函数模板特化的写法:

        1、必须得有一个主模板。

        2、template后面跟的尖括号<>中不能写任何内容。

        3、函数名后面跟尖括号<>,里面写要特化的类型。

        4、函数的形参要和主模板的形参对应。

        函数模板特化的代码:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T>
bool Less(T left, T right)//函数模板
{return left < right;
}template<>//特化
bool Less<int*>(int* left, int* right)//专门处理int* 类型的特化
{return *left < *right;
}int main()
{int a = 10;//先创建,a处于高地址int b = 20;//后创建,b的地址量级比a低cout << Less(a, b) << endl; // 可以比较,结果正确int* pa = &a;int* pb = &b;cout << Less(pa, pb) << endl; // 可以比较,结果正确return 0;
}

        运行结果:

        从结果可以看到, 即使是对指针类型的比较,结果也是符合我们所期望的。

3、类模板的特化

        上文的例子可以用类模板的方式进行大小的比较,思路是创建一个结构体Less(跟上文方法的区别在于:上文Less是一个函数),然后该结构体中实现'()'的运算符重载,目的就是使用Less的运算符重载时,让外表看起来像是在调用一个函数,也把这种操作叫做仿函数调用。

        类模板特化代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T>
struct Less//类模板
{bool operator()(T left, T right)//对()进行运算符重载{return left < right;}
};template<>
struct Less<int*>//类模板的特化
{bool operator()(int* left, int* right)//对()进行运算符重载{return *left < *right;}
};int main()
{int a = 10;//先创建,a处于高地址int b = 20;//后创建,b的地址量级比a低Less<int> ls1;cout << ls1(a, b) << endl; // 采用仿函数的方式进行a和b的比较Less<int*> ls2;int* pa = &a;int* pb = &b;cout << ls2(pa, pb) << endl; // 采用仿函数的方式进行pa和pb的比较return 0;
}

        运行结果:

3.1 偏特化

        以上的类模板特化存在局限性,即只能处理int*类型的数据,如果用该类模板去比较自定义类型的数据则编译器会执行主模板的内容,如果想让各种指针类型都能够通过该模板特化进行比较,则要用到模板的偏特化。

        偏特化的写法:

        1、在上述类模板特化的基础下,在template后面的'<>'中加上模板参数,并且该模板内的所有类型都换成模板参数。

        类模板偏特化示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T>
struct Less
{bool operator()(T left, T right)//对()进行运算符重载{return left < right;}
};template<class T>
struct Less<T*>
{bool operator()(T* left, T* right)//对()进行运算符重载{return *left < *right;}
};int main()
{int a = 10;//先创建,a处于高地址int b = 20;//后创建,b的地址量级比a低Less<int> ls1;cout << ls1(a, b) << endl; // 采用仿函数的方式进行a和b的比较Less<int*> ls2;int* pa = &a;int* pb = &b;cout << ls2(pa, pb) << endl; // 采用仿函数的方式进行pa和pb的比较string st1 = "aba";string st2 = "abc";string* pst1 = &st1;string* pst2 = &st2;Less<string*> ls3;cout << ls3(pst1, pst2) << endl;//比较的是string*类型的变量return 0;
}

         运行结果:

        因此在讲偏特化之前所采用的特化方式可以理解为:全特化,因为偏特化是对模板参数的限制,而全特化在此基础上又进行了明确的类型限制。 

3.2 全特化

        全特化就是明确模板特化的具体类型,全特化、偏特化、主模板他们三者的关系示意图如下:

        体现全特化的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T, class G>
class func
{
public:func(){cout << "template<class T, class G>" << endl;//检查编译器执行的哪个模板}
};
template<>
class func<int, char>
{
public:func(){cout << "func<int,char>" << endl;//检查编译器执行的哪个模板}
};int main()
{func<int, char> f1;func<int, int> f2;return 0;
}

         运行结果:

        从结果可以得出,如果传给类模板的类型完全符合全特化的类型,则编译器会直接调用该全特化模板。

3.3 部分特化

        部分特化即在偏特化的基础下,可以明确指定其中一个模板参数的类型,而另一个模板参数的类型不给予明确类型。但是如果明确了其中一个类型,则template的<>括号中会少一个模板参数。

        特化的总体写法示意图如下:

        部分特化的代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;template<class T, class G>
class func
{
public:func(){cout << "template<class T, class G>" << endl;//检查编译器执行的哪个模板}
};template<class T1>
class func<T1, int>
{
public:func(){cout << "func<T,int>" << endl;}
};int main()
{func<int, int> f1;func<char, char> f2;return 0;
}

        运行结果:

结语:

        以上就是关于模板特化的讲解,对于函数模板而言,模板的特化意义不大,因为可以再写一个函数模板作为原本模板的重载也可以解决特殊类型的情况。只有类模板的特化需要值得注意,实际上无非也就是分为三种情况:全特化、偏特化、部分特化,并且编译器会自动调用最匹配的一种情况。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/409994.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

散列函数,哈希表hash table

附上一句话&#xff1a;我知道大家可能曾经了解过这个散列表了&#xff0c;我发现&#xff0c;如果多看几个相关的视频&#xff0c;从不同的表述方式和不同的理解角度来理解这个问题&#xff0c;我会明白的更透彻&#xff0c;也有更多新的收获&#xff0c;尤其是对这个算法的应…

linux-nfc neard 编译与安装

项目github地址&#xff1a; https://github.com/linux-nfc/neard git clone地址&#xff1a; https://github.com/linux-nfc/neard.git clone完源码切换到目录neard里。这个项目需要依赖一下库&#xff1a; - GCC compiler - D-Bus library - GLib library - Netlink (lib…

【如何在 GitHub上面找项目】【转载】

很多的小伙伴&#xff0c;经常会有这样的困惑&#xff0c;我看了很多技术的学习文档、书籍、甚至视频&#xff0c;我想动手实践&#xff0c;于是我打开了GitHub&#xff0c;想找个开源项目&#xff0c;进行学习&#xff0c;获取项目实战经验。这个时候很多小伙伴就会面临这样的…

【Maven】008-Maven 私服搭建与使用

【Maven】008-Maven 私服搭建与使用 文章目录 【Maven】008-Maven 私服搭建与使用一、概述1、简介2、建立私服后依赖查找和下载逻辑第一步&#xff1a;请求本地仓库第二步&#xff1a;请求 Maven 私服第三步&#xff1a;请求外部远程仓库&#xff08;远程中央仓库等&#xff09…

【PostgreSQL】安装和常用命令教程

PostgreSQL window安装教程 window安装PostgreSQL 建表语句&#xff1a; DROP TABLE IF EXISTS student; CREATE TABLE student (id serial NOT NULL,name varchar(100) NOT NULL,sex varchar(5) NOT NULL,PRIMARY KEY (id) );INSERT INTO student (id, name, sex) VALUES (…

详解ISIS动态路由协议

华子目录 前言应用场景历史起源ISIS路由计算过程ISIS的地址结构ISIS路由器分类ISIS邻居关系的建立P2PMA ISIS中的DIS与OSPF中DR的对比链路状态信息的交互ISIS的最短路径优先算法&#xff08;SPF&#xff09;ISIS区域划分ISIS区域间路由访问原理ISIS与OSPF的不同ISIS与OSPF的术语…

2024美赛数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…

基于Docker的Nginx的安装与配置

基于Docker的Nginx的安装与配置 1 为Nginx创建一个容器1.1 学习docker run1.2 通过docker run为Nginx创建并启动一个容器 2 配置Nginx2.1 学习docker的bind mount技术2.2 在Nginx容器中找到想修改的文件所在的目录2.2.1 认识nginx.conf文件2.2.2 访问Nginx服务&#xff0c;默认…

TortoiseGit 2.15.0.0 安装与配置(图文详细教程)

TortoiseGit的安装与配置 TortoiseGit是Tortoise为Git提供的版本可视化工具&#xff0c;简化了记忆Git命令行的过程&#xff0c;将命令行可视化。 确保自己电脑中已经下载好了git 官网下载TortoiseGit Download – TortoiseGit – Windows Shell Interface to Git 选择64-bi…

oracle 19c容器数据库data dump数据泵传输数据(3)---完全传输

目录 查看pdb1 创建pdb2 从pdb1 中导出元数据 在pdb2中导入元数据&#xff08;dmp文件&#xff09; Full Transportable Export/Import: Example 只传输除了system&#xff0c;sysaux&#xff0c;temp&#xff0c;undo以外的用户表空间&#xff0c;這種方式傳輸的是用戶自定…

C# 图解教程 第5版 —— 第24章 预处理指令

文章目录 24.1 什么是预处理指令24.2 基本规则24.3 符号指令&#xff08;#define、#undef &#xff09;24.4 条件编译&#xff08;#if、#else、#elif、#endif&#xff09;24.5 条件编译结构24.6 诊断指令&#xff08;#warning、#error&#xff09;24.7 行号指令&#xff08;#li…

毕业设计:基于python微博舆情分析系统+可视化+Django框架 K-means聚类算法(源码)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…