模板的进阶

非类型模板参数

模板参数分类类型形参与非类型形参
类型形参即:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称
非类型形参,就是用一个常量作为类 ( 函数 ) 模板的一个参数,在类 ( 函数 ) 模板中可将该参数当成常量来使用

// 非类型模板参数,只能是整形常量
//template<class T, size_t N, double X>//err
template<class T, size_t N>
class Stack
{
public:void func(){//++N;常量不能++}
private:T _a[N];int _top;
};int main()
{//Stack<int, 10, 1.1> st1;  // 10 err//Stack<int, 20, 2.2> st2;  // 20 errStack<int, 10> st1;  // 10Stack<int, 20> st2;  // 20st1.func();return 0;
}

静态栈,非类型模板参数只能传整形常量,其他类型都不行

比如数组要开10个或者20个,有数量的时候可以这样传

注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
2. 非类型的模板参数必须在编译期就能确认结果
例子
#include<array>
#include<vector>
using namespace std;int main()
{int a2[10];array<int, 10> a1;cout << sizeof(a1) << endl;string a3[10];//a1[10];会检查越界a2[10];//检查不到越界vector<int> a4(10, 0);a4[10];//也可以检查越界return 0;
}

array是数组,第二个参数就是非类型模板参数,传10就是开辟10个空间不初始化

优势不明显,检查越界可以用vector定义,并且也能初始化,了解一下

模板的特化

函数模板特化

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;
}// 函数模板的特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}
/*
bool Less(Date* left, Date* right)
{return *left < *right;
}*///函数模板不建议特化,可以直接参数匹配来完成任务int main()
{cout << Less(1, 2) << endl;Date d1(2025, 7, 7);Date d2(2025, 7, 8);cout << Less(d1, d2) << endl; Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; Date* p3 = new Date(2022, 7, 7);Date* p4 = new Date(2022, 7, 8);cout << Less(p3, p4) << endl; return 0;
}

函数模板的特化可以理解成针对某个类型的特化,比如这里函数参数是Date*类型的时候,原来的参数匹配不行了,只能完成地址的比较,栈区地址相对大小是固定的,但是堆区地址是随机的,原生模板完成不了任务,就引入了函数模板的特化

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

可以看到这样就能完成普通模板完成不了的任务

最后不建议写函数模板的特化,因为这样写写死了,只能Date*指针才能使用

不如直接用参数匹配更简单,结果是一样的

类模板特化

全特化
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
};// 全特化
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};template <>
class Data<int, int>
{
public:Data() { cout << "Data<int, int>" << endl; }
private:
};// 偏特化1:特化部分参数
template <class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};// 偏特化2:对参数类型进行一定限制,比如:限制是指针或者引用等
template <class T1, class T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
};//两个参数偏特化为引用类型
template <class T1, class T2>
class Data <T1&, T2*>
{
public:Data(){cout << "Data<T1&, T2*>" << endl;}
};//匹配顺序//全特化 -> 偏特化 -> 原模板
int main()
{Data<int, int> d1;Data<int, char> d2;Data<char, int> d3;Data<char*, int*> d4;Data<int*, int*> d5;Data<int&, int*> d6;Data<int*, int&> d7;return 0;
}

结论,模板匹配顺序:全特化>偏特化>原模板

解析,首先要有一个原类模板

全特化也是template<>,类型后边跟两个特化类型,有传int,char的直接调用

也可以全特化为int,默认调两个int的

偏特化1:特化部分参数,保留T1,只特化第二个参数为int,只要有T,int的会直接调用

限制是两个指针,类型相同或者不同都可以

第一个传引用第二个传指针会匹配这个

好了,类模板的特化记住这些就够了,跟着敲一遍不难理解
类模板特化应用示例

template<class T>
struct Less
{bool operator()(const T& x, const T& y) const{return x < y;}
};// 偏特化
template<class T>
struct Less<T*>
{bool operator()(const T* x, const T* y) const{return *x < *y;}
};
int main()
{Date d1(2024, 7, 7);Date d2(2025, 7, 6);Date d3(2026, 7, 8);vector<Date> v1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);// 可以直接排序,结果是日期升序sort(v1.begin(), v1.end(), Less<Date>());//默认升序vector<Date*> v2;v2.push_back(&d1);v2.push_back(&d2);v2.push_back(&d3);// 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序// 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象// 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期sort(v2.begin(), v2.end(), Less<Date*>());return 0;
}
v1和v2都完成了排序的任务,因为正常比较的是日期类对象而不是指针,原生模板无法完成任务,偏特化可以完成任务,比较指针指向的对象
注意sort第三个参数是对象,所以要加括号,表示匿名对象;priority_queue优先级队列第三个参数是类型,这两个的仿函数是一样的,只不过一个传递对象,一个传递类型
这里要注意,当参数Date* /int*传递过来的时候,传递给const Date*/int*,发生类型转换生成临时变量,临时变量具有常性,这里引用的是临时对象,要么引用之前再加一个const,要么把引用去掉
这样就对了

模板分离编译

什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
如果模板声明和定义分离,有两个cpp文件
   像这种情况因为 编译 的时候两个cpp文件互相不知道对方“长啥样”(两个cpp各自有头文件的包含,头文件展开只有声明),Stack.cpp找不到要实例化的类型(不正常),test.cpp找不到模板的定义(正常,可以链接找地址,如果链接再找不到定义就会报错)
       编译阶段符号汇总,汇编阶段符号表的生成,如果函数定义要实例化的类型都没有,函数声明却有类型,二者不匹配,链接的时候符号表的合并和重定位肯定有问题,所以报了链接错误(这部分我之前写过很详细的编译链接过程,感兴趣可以点开开)
程序环境及预处理-CSDN博客https://blog.csdn.net/eixjdj/article/details/144982435https://blog.csdn.net/eixjdj/article/details/144982435https://blog.csdn.net/eixjdj/article/details/144982435 解决办法1(不推荐)
可以在Stack.cpp文件显示实例化这个类型,实战中不建议这样,太呆了
  模板定义的位置显式实例化 。这种方法不实用,不推荐使用。
解决办法2(推荐)
直接在.h定义,这样编译就能实例化成功,不需要链接时候找函数定义,这种方式是最推荐的
将声明和定义放到一个文件 "xxx.hpp" 里面或者 xxx.h 其实也是可以的 。推荐使用这种。
.hpp和.h都行.hpp是c++的.h是c的,c++本身就兼容c,所以可以这样用

源代码示例

test.cpp

#include"Stack.hpp"int main()
{Add(1, 2);func(1, 2);Add(1.1, 2.2);return 0;
}

 Stack.hpp

#pragma once
#include<iostream>
using namespace std;template<class T>
T Add(const T& left, const T& right);
void func(int a, int b);// 直接定义在.h ->调用的地方,直接就有定义,直接实例化,不需要链接时再去找
template<class T>
T Add(const T& left, const T& right)
{cout << "T Add(const T& left, const T& right)" << endl;return left + right;
}

Stack.cpp

#include"Stack.hpp"
//
//template
//double Add<double>(const double&, const double&); 
//template
//int Add<int>(const int&, const int&);void func(int a, int b)
{cout << "void func(int a, int b)" << endl;
}

模板总结

【优点】
(模板的本质是,本来应该由你来写的多份类似代码,现在不需要你重复写了,你提供一个模板,编译器根据你的实例化,帮你写出来)
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
(编译错误不好定位发现,最佳实践解决方案:排除法,一段一段注释,日常建议写一部分编译一部分)
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

以上就是模板进阶的全部内容,欢迎互相交流

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

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

相关文章

windows安装linux子系统【ubuntu】操作步骤

1.在windows系统中开启【适用于Linux的Windows子系统】 控制面板—程序—程序和功能—启用或关闭Windows功能—勾选适用于Linux的Windows子系统–确定 2.下载安装Linux Ubuntu 22.04.5 LTS系统 Ununtu下载链接 3.安装完Ununtu系统后更新系统 sudo apt update4.进入/usr/l…

【大数据技术】搭建完全分布式高可用大数据集群(Kafka)

搭建完全分布式高可用大数据集群(Kafka) kafka_2.13-3.9.0.tgz注:请在阅读本篇文章前,将以上资源下载下来。 写在前面 本文主要介绍搭建完全分布式高可用集群 Kafka 的详细步骤。 注意: 统一约定将软件安装包存放于虚拟机的/software目录下,软件安装至/opt目录下。 安…

万字详解 MySQL MGR 高可用集群搭建

文章目录 1、MGR 前置介绍 1.1、什么是 MGR1.2、MGR 优点1.3、MGR 缺点1.4、MGR 适用场景 2、MySQL MGR 搭建流程 2.1、环境准备2.2、搭建流程 2.2.1、配置系统环境2.2.2、安装 MySQL2.2.3、配置启动 MySQL2.2.4、修改密码、设置主从同步2.2.5、安装 MGR 插件 3、MySQL MGR 故…

Linux高级IO

文章目录 &#x1f965;IO的基本概念&#x1f347;钓鱼五人组&#x1f348;五种IO模型&#x1f349;高级IO重要概念同步通信 VS 异步通信阻塞 VS 非阻塞 &#x1f34a;其他高级IO&#x1f34b;阻塞IO&#x1f34b;‍&#x1f7e9;非阻塞IO &#x1f965;IO的基本概念 什么是IO…

摄像头模块烟火检测

工作原理 基于图像处理技术&#xff1a;分析视频图像中像素的颜色、纹理、形状等特征。火焰通常具有独特的颜色特征&#xff0c;如红色、橙色等&#xff0c;且边缘呈现不规则形状&#xff0c;还会有闪烁、跳动等动态特征&#xff1b;烟雾则表现为模糊、无固定形状&#xff0c;…

4.3 线性回归的改进-岭回归/4.4分类算法-逻辑回归与二分类/ 4.5 模型保存和加载

4.3.1 带有L2正则化的线性回归-岭回归 岭回归&#xff0c;其实也是一种线性回归&#xff0c;只不过在算法建立回归方程的时候1&#xff0c;加上正则化的限制&#xff0c;从而达到解决过拟合的效果 4.3.1.1 API 4.3.1.2 观察正则化程度的变化&#xff0c;对结果的影响 正则化力…

CSS outline详解:轮廓属性的详细介绍

什么是outline&#xff1f; outline&#xff08;轮廓&#xff09;是CSS中一个有趣的属性&#xff0c;它在元素边框&#xff08;border&#xff09;的外围绘制一条线。与border不同的是&#xff0c;outline不占用空间&#xff0c;不会影响元素的尺寸和位置。这个特性使它在某些…

设计模式.

设计模式 一、介绍二、六大原则1、单一职责原则&#xff08;Single Responsibility Principle, SRP&#xff09;2、开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;3、里氏替换原则&#xff08;Liskov Substitution Principle, LSP&#xff09;4、接口隔离原则&am…

硬件工程师思考笔记02-器件的隐秘角落:磁珠与电阻噪声

目录 引言 一、磁珠&#xff1a;你以为的“噪声克星”&#xff0c;可能是高频杀手 1. 磁珠的阻抗特性与误区 2. 案例&#xff1a;磁珠引发的5G射频误码率飙升 二、电阻&#xff1a;静默的噪声制造者 1. 电阻噪声的两种形态 2. 案例&#xff1a;ADC精度被电阻噪声“偷走” 三、设…

mysql 不是内部或外部命令,也不是可运行的程序或批处理文件

mysql 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 前言描述1、&#x1f331;环境变量配置&#xff08;高级系统设置&#xff09;&#xff1a;2、&#x1f331;环境变量配置&#xff08;系统属性&#xff09;&#xff1a;3、&#x1f331;环境变量配置&…

极客说|利用 Azure AI Agent Service 创建自定义 VS Code Chat participant

作者&#xff1a;卢建晖 - 微软高级云技术布道师 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&a…

在rtthread中,scons构建时,它是怎么知道是从rtconfig.h找宏定义,而不是从其他头文件找?

在rtthread源码中&#xff0c;每一个bsp芯片板级目录下都有一个 SConstruct scons构建脚本的入口&#xff0c; 在这里把rtthread tools/目录下的所有模块都添加到了系统路径中&#xff1a; 在tools下所有模块中&#xff0c;最重要的是building.py模块&#xff0c;在此脚本里面…

Redis基础--常用数据结构的命令及底层编码

零.前置知识 关于时间复杂度,按照以下视角看待. redis整体key的个数 -- O(N)当前key对应的value中的元素个数 -- O(N)当前命令行中key的个数 -- O(1) 一.string 1.1string类型常用命令 1.2string类型内部编码 二.Hash 哈希 2.1hash类型常用命令 2.2hash类型内部编码 2.3ha…

【leetcode100】岛屿的最大面积

1、题目描述 给你一个大小为 m x n 的二进制矩阵 grid 。 岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0&#xff08;代表水&#xff09;包围着。 岛屿的面积…

将仓库A分支同步到仓库B分支,并且同步commit提交

一、 问题 有一仓库A 和 一仓库B&#xff0c; 需要将仓库A分支a1所有提交同步推送到仓库B分支b1上 二、 解决 2.1、 首先需要仓库A、仓库B的权限&#xff0c; 2.2、将仓库A clone到本地&#xff0c; 进入A目录&#xff0c;并且切换到a1分支 cd A ## A 为A仓库clone到本地代…

Matplotlib基础01( 基本绘图函数/多图布局/图形嵌套/绘图属性)

Matplotlib基础 Matplotlib是一个用于绘制静态、动态和交互式图表的Python库&#xff0c;广泛应用于数据可视化领域。它是Python中最常用的绘图库之一&#xff0c;提供了多种功能&#xff0c;可以生成高质量的图表。 Matplotlib是数据分析、机器学习等领域数据可视化的重要工…

Nginx 配置 SSL(HTTPS)详解

Nginx作为一款高性能的HTTP和反向代理服务器&#xff0c;自然支持SSL/TLS加密通信。本文将详细介绍如何在Nginx中配置SSL&#xff0c;实现HTTPS的访问。 随着互联网安全性的日益重要&#xff0c;HTTPS协议逐渐成为网站加密通信的标配。Nginx作为一款高性能的HTTP和反向代理服务…

Mybatis篇

1&#xff0c;什么是Mybatis &#xff08; 1 &#xff09;Mybatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁…

文件上传全详解

前言 我们下面进行下一个漏洞——文件上传的学习。文件上传是常见漏洞之一&#xff0c;是Web安全必学漏洞。为探讨清楚文件上传漏洞的诸多细节&#xff0c;我们特以经典的upload-labs进行从入门到进阶的专项训练。 作者进行upload-labs靶场练习时&#xff0c;在环境配置上出了…

【centOS】搭建公司内网git环境-GitLab 社区版(GitLab CE)

1. 安装必要的依赖 以 CentOS 7 系统为例&#xff0c;安装必要的依赖包&#xff1a; sudo yum install -y curl policycoreutils openssh-server openssh-clients postfix sudo systemctl start postfix sudo systemctl enable postfix2. 添加 GitLab 仓库 curl -sS https:/…