【C++杂货铺】模板进阶

news2024/11/19 9:19:30


目录

🌈前言🌈

📁 泛型编程

📁 函数模板

 📂 概念

 📂 格式

 📂 class 和 typename

 📂 原理

 📂 函数模板实例化

 📂 匹配原则

📁 类模板

 📂 格式

 📂 实例化

📁 非类型模板参数

📁 模板特化

 📂 概念

 📂 函数模板的特化

 📂 类模板的特化

1. 全特化

2. 半特化/偏特化

📁 模板的分离编译

 📂 概念

 📂 模板的分离编译

 📂 解决方法

📁 模板总结

📁 总结


🌈前言🌈

        欢迎收看本期【C++杂货铺】,本期内容讲解C++中重要语法之一——模板,模板的使用,解决了我们需要重复做的工作,大大提高了我们开发效率。接下来,让我们看看模板的一些概念吧。其中包括了模板的分类,以及一些特殊用法。

        如果你还没有模板基础,可以快速阅览下面这篇文章,当然,本文也会简单介绍模板初阶的一些基本内容。

【C++杂货铺】模板-CSDN博客

📁 泛型编程

        在C++中,存在一个模具,通过给这个模具填充不同类型,来获得不同的具体类型的编码。

        泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。

        其实,模板跟函数是有很大相同点,

        1. 函数传参,传递的是对象;模板传参,传递的是类型

        2. 函数是在运行时传参;模板是在编译时传参(模板需要在编译时实例化)

        

📁 函数模板

 📂 概念

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

 📂 格式

template<class T1,class T2...>
返回值类型  函数名(参数列表)
{
    函数体
}

 📂 class 和 typename

        其中class 可以和typename互换,两者本质上没有区别。只是在某些场景下,必须使用typename,来声明是模板类。 

        class 和 typename 都是用于声明模板参数,在大多数情况下都是等价的,在某些情况下有微小的差异。

        在模板内部中,当引用模板参数时,如果是一个类型,可以使用class或typename。但当你引用一个模板类型参数时,必须使用typename,因为编译器无法确定这个模板类型参数是否为类型。

template <typename T>
void foo() {
    typename T::SomeType* ptr;  // 此处必须使用 typename
}

        这里T::someType是一个类型依赖于模板参数T,因此必须使用typename来告诉编译器T::someType是一个类型而不是成员变量或函数。

 📂 原理

        函数模板是一个蓝图,本身并不是函数,是编译器根据使用方式产生特定具体类型函数的模具,所以模板将本来我们应该做的重复事情交给了编译器干。

        在编译期间,编译器根据传入的实参类型推演生成实参类型队形类型的函数。

 📂 函数模板实例化

 隐式实例化:编译器根据实参推演模板函数的实际类型。

 显示实例化:在函数名后的<>中指定模板参数的实际类型。

 📂 匹配原则

1. 非模板函数可以和一个同名的函数模板同时存在,且该函数模板被实例化为这个非模板函数;

2. 非模板函数和同名函数模板,如果其他条件相同,在调动时有点调用非模板函数,如果模板可以产生一个更好的匹配函数,则会选择模板

📁 类模板

 📂 格式

template<class T1,class T2...>
class 类模板名称
{
    类成员定义
};

 📂 实例化

        类模板实话与函数模板实例化不同,类模板实例化需要在类模板名字后面跟<>,然后将实例化的类型放在<>即可,类模板名称不是真正的类,实例化的结果才是真正的类。

vector<int> v1;
vector<double> v2;

        以上就对模板初阶做了一遍回顾,总体上来说,介绍了函数模板和类模板的概念,以及使用方式,拓展了class和typename的细微差异。

📁 非类型模板参数

        模板参数分 类型形参非类型形参。此外模板参数可以跟函数一样,有缺省参数。

        类类型形参:出现在模板参数列表中,跟在class或typename之类的参数类型名称。

        非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板汇总可以将参数当成常量来使用。

 template<class T, size_t N = 10>
 class array
 {
 public:
     T& operator[](size_t index){return _array[index];}
     const T& operator[](size_t index)const{return _array[index];}
 
     size_t size()const{return _size;}
     bool empty()const{return 0 == _size;}
 
 private:
     T _array[N];
     size_t _size;
 };

注意:

        1. 在大多数版本下,浮点数,类对象以及字符串是不允许作为非类型模板参数的。

        2.  非类型模板参数必须在编译期间就能确认结果。

📁 模板特化

 📂 概念

        通常情况下,使用模板可以实现与类型无关的代码,但对于一些特殊特殊类型可能会得到一些错误结果,需要特殊处理。

// 函数模板 -- 参数匹配
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是能正常比较的,但在特殊场景下,就得到错误结果,例如p1和p2,Less内部没有比较p1和p2指向的对象内容,而是比较的p1和p2指针的地址,无法到达预期而错误。

        此时,需要对模板进行特化,即在原模板的基础上,针对特殊类型进行特殊化的实现方式,模板特化分为,函数模板特化和类模板特化。

 📂 函数模板的特化

函数模板的特化步骤:
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;
}

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

bool Less(Date* left, Date* right)
{
 return *left < *right;
}
        该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。

 📂 类模板的特化

1. 全特化

全特化即是将模板参数列表中所有的参数都确定化。
template<class T1, class T2>
class Data
{
public:
 Data() {cout<<"Data<T1, T2>" <<endl;}
private:
 T1 _d1;
 T2 _d2;
};
template<>
class Data<int, char>
{
public:
 Data() {cout<<"Data<int, char>" <<endl;}
private:
 int _d1;
 char _d2;
};
void TestVector()
{
 Data<int, int> d1;
 Data<int, char> d2;
}

2. 半特化/偏特化

任何针对模版参数进一步进行条件限制设计的特化版本。
template<class T1, class T2>
class Data
{
public:
 Data() {cout<<"Data<T1, T2>" <<endl;}
private:
 T1 _d1;
 T2 _d2;
};

偏特化有两种表现形式:

1. 部分特化

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
 Data() {cout<<"Data<T1, int>" <<endl;}
private:
 T1 _d1;
 int _d2;
};

2. 参数更进一步的限制

        偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{ 
public:
 Data() {cout<<"Data<T1*, T2*>" <<endl;}
 
private:
 T1 _d1;
 T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
 Data(const T1& d1, const T2& d2)
 : _d1(d1)
 , _d2(d2)
 {
 cout<<"Data<T1&, T2&>" <<endl;
 }
 
private:
 const T1 & _d1;
 const T2 & _d2; 
 };
void test2 () 
{
 Data<double , int> d1; // 调用特化的int版本
 Data<int , double> d2; // 调用基础的模板 
 Data<int *, int*> d3; // 调用特化的指针版本
 Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

📁 模板的分离编译

 📂 概念

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

 📂 模板的分离编译

// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
 return left + right;
}
// main.cpp
#include"a.h"
int main()
{
 Add(1, 2);
 Add(1.0, 2.0);
 
 return 0;
}

 📂 解决方法

1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者 xxx.h 其实也是可以的 。推荐使用这种。
2. 模板定义的位置显式实例化 。这种方法不实用,不推荐使用。

📁 模板总结

【优点】
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

📁 总结

        以上,我们就对模板初阶做了回顾,模板进阶进行了讲解,其中包含了非类型模板参数,就是在模板内部当做常量使用;讲解了模板的特化,即针对特殊类型,产生对应类型的模板实例化,防止产生错误。

        还拓展了模板的分离编译,得出结论,不要讲模板进行分离编译,这会导致模板定义无法实例化,从而导致无法使用。

        如果感觉本期内容对你有帮助,欢迎点赞,收藏,关注Thanks♪(・ω・)ノ

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全球历年GDP增长率_探数API数据统计

以下是数据的详细说明&#xff1a; 全球GDP增长最快的年份是1964年&#xff0c;全球GDP增速达到6.65%。2021年的GDP增长率也相当高&#xff0c;主要受2020年衰退后的恢复性增长推动。 全球GDP增长最慢的年份包括&#xff1a;1974年、1975年&#xff08;第一次石油危机引发&…

clion最新安装教程

还在用Dev-C吗&#xff1f;也尝试了很多C编辑器&#xff0c;不是太老&#xff0c;就是太复杂。对于c开发者来说clion真的好用&#xff0c;CLion是一款专为开发C及C所设计的跨平台IDE。难受的是cion并不免费&#xff0c;仿佛是在证明好货不贵的道理&#xff0c;只能免费用30天。…

2024年阿里云优惠券领取攻略

阿里云作为国内领先的云计算服务提供商&#xff0c;以其稳定、高效、安全的服务赢得了众多用户的青睐。为了吸引用户上云&#xff0c;阿里云经常推出各种优惠活动&#xff0c;其中就包括阿里云优惠券。本文将为大家详细解读2024年阿里云优惠券的领券攻略&#xff0c;帮助大家轻…

【WinForm】如何在自己的程序窗口中显示并调用外部桌面程序

当你爱上一个程序的功能&#xff0c;并且希望扩展它以满足自己的需求时&#xff0c;你可能会觉得困惑。毕竟&#xff0c;你已经为此付出了很多努力&#xff0c;并希望能够有效地整合这些功能。那么&#xff0c;是否可以将这些功能嵌套到自己的程序中呢&#xff1f; 首先&#…

【操作系统专题】详解操作系统 | 操作系统的目标和功能 | 操作系统如何工作

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.操作系统的目标和功能2…

计算机炸了,电子信息也是劝退专业?

还不是因为这个版本&#xff0c;计算机专业受到了制裁&#xff0c;导致这些偏计算机类的专业也受到了牵连 我本科的时候是一所双一流院校的计科专业&#xff0c;我们学校的电子信息专业堪称苦逼&#xff0c;我们计科学的东西&#xff0c;他们都要学&#xff0c;他们学的一些东…

如何使用 LangChain 构建基于LLMs的应用——入门指南

大型语言模型(LLMs)是非常强大的通用推理工具&#xff0c;在各种情况下都非常有用。 但是&#xff0c;与构建传统软件不同&#xff0c;使用LLMs存在一些挑战&#xff1a; 调用往往是长时间运行的&#xff0c;并且随着可用输出而逐步生成输出。与固定参数的结构化输入&#xf…

力扣面试150 整数转罗马数字 打表 + 贪心

Problem: 12. 整数转罗马数字 文章目录 思路&#x1f496; 打表 贪心 思路 &#x1f468;‍&#x1f3eb; 参考&#xff1a;打表贪心 &#x1f496; 打表 贪心 class Solution {public String intToRoman(int num) {int[] value {1000, 900, 500, 400, 100, 90, 50, 40, …

微服务之Consul 注册中心介绍以及搭建

一、微服务概述 1.1单体架构 单体架构&#xff08;monolithic structure&#xff09;&#xff1a;顾名思义&#xff0c;整个项目中所有功能模块都在一个工程中开发&#xff1b;项目部署时需要对所有模块一起编译、打包&#xff1b;项目的架构设计、开发模式都非常简单。 当项…

MGRE环境下运行OSPF

一、分析要求 自行定义公网网段和私有网段&#xff0c;ISP设备仅配置IP地址R1/R4/R5构建Full-Mesh结构R1/R2/R3构建Hub-Spoke结构&#xff0c;R1为NHS除ISP设备&#xff0c;其余路由器运行OSPF 二、实施过程 1. 配置IP及环回地址 R1 [R1]int g 0/0/0 [R1-GigabitEthernet0/…

运筹说 第112期 | M/M/s等待制排队模型

通过上期学习&#xff0c;大家已经了解了排队论中的一些基本概念&#xff0c;以及生灭过程和Poisson过程。 那么本期小编将基于这些基本原理&#xff0c;为大家介绍M/M/s混合制排队模型&#xff0c;包括单服务台模型和多服务台模型&#xff0c;介绍模型的概念以及推导过程等内容…

全栈的自我修养 ———— react实现滑动验证

实现滑动验证 展示依赖实现不借助create-puzzle借助create-puzzle 展示 依赖 npm install rc-slider-captcha npm install create-puzzleapi地址 实现 不借助create-puzzle 需要准备两张图片一个是核验图形&#xff0c;一个是原图------> 这个方法小编试了后感觉比较麻烦…

1panel更新系统

准备两个软件包 名为dist的前端包 以.jar为后缀的后端jar包 更新后端 进去1Panel管理页面(浏览器收藏里有) http://127.0.0.1:42689/f2a8a874bd 点击容器&#xff0c;将名为app的容器直接删除掉 打开软件electerm 点击书签&#xff0c;连接2222 连接成功后长这样&#xff…

咸鱼之王_手游_开服搭建架设_内购修复无bug运营版

视频演示 咸鱼之王_手游_开服 游戏管理后台界面 源码获取在文章末尾 源码获取在文章末尾 源码获取在文章末尾 或者直接下面 https://githubs.xyz/y28.html 1.安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh &…

软考之零碎片段记录(十五)+复习巩固(十)

一、学习 1. 多对多关系模式 举例&#xff1a;学生和课程。顾客和商品等。 多对多关系的确立需要有中间表&#xff0c;需要使用两个外键确认表中的唯一数据。 2. 数据库范式 1nf 表中每个字段都是原子性不可查分的。在关系&#xff08;或表&#xff09;中&#xff0c;每一行…

软件设计师——软件工程基础知识

软件工程基础知识 软件过程软件过程模型软件测试方法进度管理软件复杂性度量环路复杂度耦合聚合和组合 软件过程 软件过程模型 软件测试方法 黑盒测试和白盒测试 白盒测试中&#xff0c;语句覆盖对程序执行逻辑的覆盖很低&#xff0c;因此一般认为它是很弱的逻辑覆盖。 进度管…

微服务架构使用和docker部署方法(若依)

这里以若依官方网站开源的微服务框架为例子记录使用方法过程。 开源地址&#xff1a;RuoYi-Cloud: &#x1f389; 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统&#xff0c;同时提供了 Vue3 的版本 下载后&#xff0c;用IDEA社区版开发工具打…

Vol.41 SEO基本术语解释

1.TDK TDK&#xff1a;即标题、描述、关键词&#xff1b;TDK是网站的基本属性&#xff0c;对SEO非常重要&#xff0c;可以帮助搜索引擎了解你的网站&#xff1b; T&#xff1a;title 谷歌建议不超过70个字符 D&#xff1a;description 谷歌建议不超过150个字符 K&#xff1…

麒麟服务器操作系统安装DHCP服务02

原文链接&#xff1a;麒麟服务器操作系统安装DHCP服务02 Hello&#xff0c;大家好啊&#xff01;继昨天介绍了在麒麟服务器操作系统上部署DHCP服务并演示了终端自动获取IP地址的过程之后&#xff0c;今天我们将进一步探讨如何通过绑定终端的MAC地址来为其分配固定的IP地址。这种…

【一竞技DOTA2】ESL ONE伯明翰站分组名单出炉

1、ESL ONE伯明翰站即将在4月22号开战,小组赛分组名单也正式出炉。 A组队伍名单:BB、G2xiG、SR、Talon、Falcons、Liquid B组队伍名单:GG、Heroic、OG、Spirit、Tundra、XG 小组赛是常规Bo2循环赛(4月22日-4月24日)各个小组的前两名晋级胜者组,三四名晋级败者组,五六名被淘汰…