一、非类型模板参数

模板参数  分为  类型形参与  非类型形参
类型形参:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常 量来使用
注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
2. 非类型的模板参数必须在编译期就能确认结果。
3. 类型形参 和 非类型形参都可以给缺省值。

 非类型形参有什么用 ? 

用宏定义的N 实现的静态栈对比 :

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stack>
using namespace std;#define N 20template<class T>
class Stack
{
private:T _a[N];int _top;
};int main()
{Stack<int> st1;  //20 return 0;
}

 

此时建立的Stack 的大小只能为N , N如果为 20 , 仅能建立大小为 20 的静态栈 。如果借助非类型形参  , 则静态栈建立的大小就变得灵活了

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stack>
using namespace std;template<class T,size_t N>
class Stack
{
private:T _a[N];int _top;
};int main()
{Stack<int,20> st1;  //20 Stack<int, 2000> st2;   //2000return 0;
}

非类型的形参一般来说 , 给整型常量 , 其他的可能不支持 , 浮点常量在C++20支持 。

二、模板的特化

2.1 概念

通常情况下使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到
一些 错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stack>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}// 函数模板 -- 参数匹配
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指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内
而比较的是p1和p2指针的地址, 这就无法达到预期而错误。

 

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

2.2 函数模板特化

函数模板特化的步骤:

  • 必须要有一个基础的函数模板
  • 关键字 template  后面接 一对 空的尖括号 <>
  • 函数名 后跟一对尖括号 , 尖括号中指定需要特化的类型
  • 函数形参表 : 必须要和模板函数的基础参数类型完全相同 , 如果不同编译器可能会报一些奇怪的错误 。 
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stack>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}// 函数模板 -- 参数匹配
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;
}

 该种实现简单明了 , 代码的可读性高 , 容易书写 因为对于一些参数类型复杂的函数模板 , 特化时会比较复杂 。 因此函数模板不建议特化 。 

2.3 类模板特化

2.3.1 全特化

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

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stack>
using namespace std;class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}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.3.2 偏特化 

1) 任何针对模版参数进一步进行 条件限制 设计的特化版本。:
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//两个参数偏特化为指针类型
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); // 调用特化的指针版本
}

 

2)部分特化:将模板参数类表中的  一部分参数  特化
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};

 

三、模板分离编译

3.1 什么是分离编译

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

3.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;
}

为什么链接错误 ? (通常是指有声明无定义) 

3.3 解决方法

 1. 将声明和定义放到一个文件 “ xxx.hpp ” 里面 或者 xxx.h 其实也是可以的 。(推荐)

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

3.4 模板总结

【优点】 

  1. 模板复用了代码,节省资源,更快迭代开发,C++的标准模板库(STL)因此产生。
  2. 增强了代码的灵活性

【缺点】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

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

相关文章

【质量管理】软件缺陷管理实施方案(专业版)

引言 方案目标与范围 本方案以CMMI量化管理要求与ISO 9000质量体系为框架,核心目标是通过标准化缺陷管理流程实现缺陷全生命周期可控。具体包括:确保软件缺陷在全生命周期中被及时发现与修复,减少其对软件质量、发布计划及用户体验的负面影响;以“零缺陷”为首要目标,针对…

Elasticsearch 讲解及 Java 应用实战:从入门到落地

在数据量爆炸的今天&#xff0c;传统数据库的查询能力越来越难以满足复杂的检索需求。比如电商平台的商品搜索&#xff0c;需要支持关键词模糊匹配、多条件筛选、热门度排序等功能&#xff0c;这时候 Elasticsearch&#xff08;简称 ES&#xff09;就成了最佳选择。作为一款分布…

docker pull weaviate 国内拉取失败的问题

我是校内网&#xff0c;尝试了 改镜像源 (cooragent) ruiyCJQ:~/sdb/B/cooragent$ sudo vim /etc/docker/daemon.json [sudo] password for ruiy: (cooragent) ruiyCJQ:~/sdb/B/cooragent$ sudo service docker restart (cooragent) ruiyCJQ:~/sdb/B/cooragent$ sudo docke…

Vue项目使用Univer Sheets

Univer Excel主页链接&#xff1a;安装步骤 1. 安装 使用预设模式的包管理器安装 - 预设模式&#xff1a;可以理解为开包即用的模式&#xff0c;省去很多配置&#xff0c;当然自由度不如插件模式 pnpm add univerjs/presets univerjs/preset-sheets-core2. 前端代码 <te…

Python day24

浙大疏锦行 python day24 内容&#xff1a; 元组&#xff1a;类比于列表&#xff0c;不过元组的元素不能被修改&#xff0c;显示也是从[]改为了()&#xff0c;其余操作则是和列表类似&#xff0c;且元组是有序的可迭代对象&#xff1a;即可以使用迭代器访问的对象&#xff0c…

Three.js 动画系统入门:Tween.js 与 AnimationMixer 的使用

引言 动画是 Three.js 中增强 3D 场景动态效果的核心技术&#xff0c;能够为用户带来沉浸式体验。Three.js 支持通过 Tween.js 实现简单的属性动画&#xff0c;以及通过 AnimationMixer 处理复杂的混合动画和骨骼动画。本文将深入探讨如何使用 Tween.js 控制 Object3D 的属性动…

装修进度管理系统功能对比:主流工具9选

本文分享了9款常用的装修进度管理软件&#xff0c;包括&#xff1a;1.Worktile&#xff1b;2.中望软件&#xff1b;3.三维家&#xff1b;4.Procore&#xff1b;5.易达装修管理系统&#xff1b;6.装修管家&#xff1b;7.Zoho Projects&#xff1b;8.中建君联&#xff1b;9.一品装…

深度学习篇---预训练模型

在深度学习中&#xff0c;预训练模型&#xff08;Pretrained Model&#xff09; 是提升开发效率和模型性能的 “利器”。无论是图像识别、自然语言处理还是语音识别&#xff0c;预训练模型都被广泛使用。下面从概念、使用原因、场景、作用等方面详细介绍&#xff0c;并结合 Pyt…

Redis ①⑦-分布式锁

分布式锁 分布式锁是锁的一种&#xff0c;都是为了解决多线程/多进程环境下&#xff0c;对共享资源的访问冲突问题。 不过&#xff0c;像 Java 的 synchronized 或者 C 的 mutex 这种锁&#xff0c;都是进程内的锁&#xff0c;而分布式锁则是跨越进程/机器的锁。也就是可以针对…

OpenCV-图像预处理➀【图像颜色空间转换、灰度化实验、二值化处理、镜像翻转 和 仿射变换】

文章目录先言一、图像颜色空间转换1.RGB颜色空间2.颜色加法3.颜色加权加法4.HSV颜色空间5.图像转换&#xff08;cvtColor()&#xff09;二、灰度实验1.灰度图2.图像灰度化&#xff08;最大值法&#xff09;3.图像灰度化&#xff08;平均值法&#xff09;4.图像灰度化&#xff0…

APP逆向 day9 安卓开发基础

一.前言 app逆向当然要学安卓基础啦&#xff01;今天我们来教安卓基础当然&#xff0c;安卓基础不会教的很多&#xff0c;比java还要少&#xff0c;还是那句话&#xff0c;了解就好。 二.安卓环境搭建 2.1 安卓介绍 如果做安卓开发 需要会java代码安卓SDK(安卓提供的内置…

Jmeter的元件使用介绍:(三)配置元件详解02

六、计数器 可以用来做一些变量自增操作。 1、Starting value:定义初始值 2、递增&#xff1a;定义每次执行递增多少 3、Maximum value:定义承受的最大值 4、数据格式&#xff1a;可以不填&#xff0c;也可以定义成000;001;002等等任意格式都行。&#xff08;1&#xff09;如…

JavaWeb学习打卡15(JSP标签、JSTL标签、EL表达式)

EL表达式&#xff1a;${ }获取数据执行运算获取web开发的常用对象在pom.xml 文件中导入JSP、JSTL相关依赖&#xff1a;<!--JSP依赖--><!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --><dependency><groupId>java…

7.22数据结构——顺序表

文章目录一、思维导图二、实现顺序表的功能代码head.htest.cmain.c一、思维导图 二、实现顺序表的功能代码 head.h #ifndef __HEAD_H__ #define __HEAD_H__#include <stdio.h> #include <string.h> #include <stdlib.h> //数组的最大长度 #define MAXSIZE …

【如何无限制免费试用 IDEA || Pycharm(JB 全家桶)】

如何无限制免费试用 IDEA || Pycharm(JB 全家桶) 一、目标:解决 JB 全家桶试用时长痛点 如果你是程序员,大概率用过 JetBrains 家的 IDE——IDEA 写 Java、Pycharm 写 Python、WebStorm 做前端,体验确实顶流,但官方 30 天试用到期后,动辄几千的年费实在让人肉痛。 咱…

Qt(资源库和按钮组)

这一节是对上一节的补充&#xff0c;上一节提到QLabel类和QAabstractButton类&#xff0c;这节内容&#xff1a;1.如设置资源库&#xff0c;使用资源设置图片2. 使用按钮组管理多个按钮。一、资源库1. 资源库作用Qt的资源库&#xff08;Resource System&#xff0c;.qrc文件&am…

一道检验编码能力的字符串的题目

#include<iostream> #include<vector> #include<string> using namespace std; int bNum0,gNum0; int findEnd(string& s,int si){int lens.size();//当前字母在哪个字符串中,存入comp中string comp;if(s[si]b||s[si]o||s[si]y){comp"boy";bNu…

UniApp X 网络请求避坑指南:从 JS 到 UTS 的 JSON 数据处理全解析

在 UniApp 开发中&#xff0c;我们经常需要通过 uni.request 获取服务器返回的 JSON 数据&#xff0c;并将其绑定到页面或进行逻辑处理。但在 UniApp X&#xff08;基于 UTS&#xff09; 中&#xff0c;由于引入了 强类型语言特性&#xff0c;处理 JSON 数据的方式与 JS 有明显…

iOS 网络请求常用依赖库与系统自带 API 介绍与示例

iOS 网络请求常用依赖库与系统自带 API 介绍与示例 在 iOS 开发中&#xff0c;进行网络请求是几乎所有应用都不可或缺的功能。开发者有多种选择来处理网络通信&#xff0c;从系统自带的 URLSession 到各种流行的第三方库。下面我将为您介绍 URLSession、AFNetworking、Alamofir…

JavaScript 中 let 在循环中的作用域机制解析

一、let在循环中的特殊性 let作为ES6引入的块级作用域声明&#xff0c;在循环结构中存在特殊行为&#xff0c;其核心区别于var的函数作用域特性。理解这一特性对于编写正确的闭包逻辑至关重要。 在 ECMAScript 规范里&#xff0c;let声明的变量具有块级作用域特性&#xff0c;这…