深入理解模板进阶:掌握C++模板的高级技巧

🎉个人名片:

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉 ————————————————

🎉文章简介:

🎉本篇文章将 C++模板进阶,全特化,偏特化,非类型模板参数,模板的分离编译 相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
——————————————————

一.文章前言

上次将模初阶的学习知识进行了分享(链接: link),今天在这篇文章中你将学习到一些关于C++模板进阶的一些知识,包括模板特化和偏特化:介绍如何通过特化和偏特化来为特定类型提供定制化的模板实现,以及如何处理模板的重载和优先级问题。

二.模板

一. 非类型模板参数

首先,模板参数可以分为类类型形参和非类型形参

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

非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用;

就比如:我们实现一个静态的栈的时候,定义一个静态数组,假设数组大小为N,N被#define为20,那么当我们想要实现一个大小为20的栈和一个大小为1000的栈时,只能被迫去改N的大小,但是如果改为1000的,栈空间为20的那个栈空间就会浪费,这个时候就可以使用非类型模板参数;

例如:

//假设实现一个静态栈
#define N 20
template<class T>
class stack
{
public:stack(){cout << "stack()" << endl;}
private:T _a[N];size_t _size;
};
int main()
{stack<int> stack1;   //20stack<int> stack2;   //100return 0;
}

解决方法:使用非类型模板参数

//使用非类型模板参数,整型常量
template<class T,size_t N>
class stack
{
public:stack(){cout << "stack()" << endl;}
private:T _a[N];size_t _size;
};
int main()
{stack<int,20> stack1;   //20stack<int,1000> stack2;   //1000return 0;
}

注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果

例如:
在这里插入图片描述

二. 模板的特化

使用模板实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要进行特殊处理的方法;

例子:
//实现了一个日期类

class Date
{
public:Date(int year,int month,int day):_year(year),_month(month),_day(day){}bool operator>(Date& dd){if (_year > dd._year)return true;else if(_year == dd._year && _month > dd._month)return true;else if(_year == dd._year && _month == dd._month && _day > dd._day)return true;elsereturn false;}
private:int _year;int _month;int _day;
};

我们想要这样做比较时就会出错;

template<class T>
bool great(T x,T y)
{return x > y;
}
int main()
{Date d1(1010, 5, 3);Date d2(1010, 1, 2);cout << great(d1, d2)<< endl;Date* p1 = &d1;Date* p2 = &d2;   //当我们只有日期类对象的指针时,想要比较时cout << great(p1, p2) << endl;return 0;
}

解析:

如图:当我们调用函数1时,参数传的是一个日期类对象,T是一个日期类对象,当在>比较的时候,因为Date是自定义类型,就会去调用他的>运算符重载,实现比较。
但是当在调用第二个函数时,我们想的是对指针指向的对象进行比较,但是函数参数传的是Date的指针,great函数会实例化生成一个Date*的函数,去调用生成的函数,达不到想要的效果;
在这里插入图片描述

一.函数模板的特化

为了解决上面的问题:

1.我们可以使用仿函数类解决(仿函数在上篇分享过,不知道的可以去看看链接: link)

2.函数模板的实例化

template<class T>
bool great(T& x,T& y)
{return x > y;
}
//函数特化
template<>
bool great<Date*>(Date*& x, Date*& y)
{return *x > *y;
}

3.根据编译器的匹配机制再写一个函数即可这 ;

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

template<class T>
bool great(T& x,T& y)
{return x > y;
}
//
bool great(Date* x, Date* y)
{return *x > *y;
}

类模板的特化

类模板的特化分为:全特化和偏特化

全特化

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

template<class T1, class T2>
class Data
{
public:Data() { }
private:T1 _d1;T2 _d2;
};//特化后
template<>
class Data<int, char>
{
public:Data() { }
private:int _d1;char _d2;
};
void test()
{Data<int, int> d1;   //调用原模版Data<int, char> d2;   //调用特化后的
}
偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本

偏特化也有两种:
第一种:部分特化
将模板参数列表中部分特化

//原模版
template<class T1, class T2>
class Data
{
public:Data() { }
private:T1 _d1;T2 _d2;
};
//特化后
template<class T1>
class Data<T1,char>
{
public:Data(){ }
private:T1 _d1;char _d2;
};

第二种:参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本;

例如下面的例子,特化后只能接受指针类型;

//原模版
template<class T1, class T2>
class Data
{
public:Data() { }
private:T1 _d1;T2 _d2;
};
//特化后
template<class T1,class T2>
class Data<T1*,T2*>
{
public:Data() { }
private:T1 _d1;T2 _d2;
};

三. 模板的分离编译

什么是分离编译?

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

当我们写一个函数模板时,.h放声明,.cpp放定义时,例如:

在这里插入图片描述

最后编译器会报链接错误;

这是因为:
C/C++程序要运行,一般会经历预处理–>编译–>汇编–>链接这四个步骤,

预处理:
主要是头文件的展开,这里的话Test.h,会在main.cpp与Test.cpp里面展开,生成main.i与Test.i,因为头文件展开了,就没有头文件了;
展开了过后Test.i里面既有函数声明也有函数定义;main.i里面有函数声明,没有定义;

编译:
检查语法,实例化模板等操作,如果没有错误后会形成汇编代码;生成main.s与Test.s;实例化模板的时候,这里不知道实例化为什么类型的函数没所以这里并没有实例化函数;

汇编:
生成二进制的机器语言;生成mian.o与Test.o

链接:会将这两个文件合并到一起,到这里会发现函数找不到函数的地址,这两个文件前面3不都是分离的,没有交互,mian.i里面知道将func函数模板中T实例化为int,但是没有函数的定义,只有声明,Test.i可以实例化生成函数,但是不知道将T实例化为什么类型,所以到最后的合并后,没有函数地址,报链接错误;

最好的解决方法,函数模板就不要声明定义分离;

请添加图片描述

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

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

相关文章

Reactor线程模型

线程模型 一、背景1.socket网络通信2.IO模型与线程模型3.线程模型分类3.1 阻塞模型3.2 Reactor模型3.3 Proactor模式 二、阻塞模型1.代码示例 三、Reactor模型1.单Reactor单线程1.1 处理过程1.2 优缺点1.3 代码示例 2.单Reactor多线程2.1 处理机制2.2 优缺点 3.主从Reactor3.1 …

Ubuntu Desktop 设置 gedit

Ubuntu Desktop 设置 gedit 1. View2. Editor3. Font & Colors4. keyboard shortcut5. Find and ReplaceReferences gedit (/ˈdʒɛdɪt/ or /ˈɡɛdɪt/) is the default text editor of the GNOME desktop environment and part of the GNOME Core Applications. Desig…

[蓝桥杯 2015 省 B] 生命之树

水一水的入门树形DP #include<iostream> #include<algorithm> #include<vector> using namespace std; using ll long long; #define int long long const int N 2e610; const int inf 0x3f3f3f3f; const int mod 1e97;int n; int w[N]; vector<vecto…

Unity双击全屏UI按钮、长按UI按钮

1.长按按钮 将下面的代码挂载到按钮上&#xff0c;去掉按钮本射的Button组件 using System.Collections; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems;public class LongPressButton : MonoBehaviour, IPointerDownHandler, IPointerUpHand…

递推与递归

92. 递归实现指数型枚举 - AcWing题库 #include <bits/stdc.h> using namespace std; const int N17; int n; bool vis[N];//记录某一个数是否出现过 void dfs(int dep){// if(vis[dep])continue;//没有这一句 因为一定不会有已经选过的数if(depn1){//对于每个数都做完了…

NCP1031DR2G集成DC-DC转换器 为以太网供电设备提供低成本、高可靠性解决方案

NCP1031DR2G是微型高电压单片 DC-DC 转换器&#xff0c;带有片上电源开关和启动电路。使用极少的外部部件&#xff0c;将实施若干开关稳压器应用所需的所有有源电源、控制逻辑和保护电路相结合&#xff0c;这些应用有辅助侧偏置电源和低功率 DC-DC 转换器。此控制器系列适用于 …

Learn OpenGL 17 立方体贴图

立方体贴图 我们已经使用2D纹理很长时间了&#xff0c;但除此之外仍有更多的纹理类型等着我们探索。在本节中&#xff0c;我们将讨论的是将多个纹理组合起来映射到一张纹理上的一种纹理类型&#xff1a;立方体贴图(Cube Map)。 简单来说&#xff0c;立方体贴图就是一个包含了…

OpenCV Steger算法提取条纹中心线

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 Steger 算法是一种常用的图像边缘检测算法,可以用于提取图像中的中心线或边缘信息。它的理论假设是:条纹的亮度是按照高斯分布呈现的,即中心亮两侧渐暗。 其计算过程如下所述: 1、首先,我们需要计算每个点Hess…

Kali Linux结合cpolar内网穿透实现公网环境SSH远程访问

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过[cpolar 内网穿透](cpolar官网-安全的内网穿透工具 | 无需公网ip | 远程访问 | 搭建网站)软件实现ssh 远程连接kali! …

鸿蒙开发实战案例—QQ联系人开发教程

本次案例挑选了QQ4个tab中相对较难的一个页面进行开发 先看一下本次案例的最终效果图&#xff1a; 这个页面的难点在于在List组件又嵌套了一个可以左右滑动的联系人分类&#xff0c;以及好友分组的展开和收起。 现在我们从上到下来开发这个界面&#xff0c;首先是导航栏&…

适合一个人开的实体店:轻松创业,快速盈利的秘诀

在当今的创业潮流中&#xff0c;很多人都渴望拥有一份属于自己的事业。如果你也想独自创业&#xff0c;开一家实体店&#xff0c;那么这篇文章将为你提供一些有价值的干货信息。作为一名经营5年酸奶吧的创业者&#xff0c;我将分享一些经验和见解&#xff0c;帮助你轻松创业并实…

【MD】金属-半导体界面超快辐射诱导熔化的分子动力学模拟

这篇文章是一篇发表在《Journal of Applied Physics》上的论文&#xff0c;标题为“Molecular dynamics simulations of ultrafast radiation induced melting at metal–semiconductor interfaces”&#xff0c;作者为Ashwin Ravichandran, Mohit Mehta, Andrew A. Woodworth,…

Python爬虫案例-爬取主题图片(可以选择自己喜欢的主题)

2024年了&#xff0c;你需要网络资源不能还自己再慢慢找吧&#xff1f; 跟着博主一块学习如何利用爬虫获取资源&#xff0c;从茫茫大海中寻找那个她到再妹子群中找妹子&#xff0c;闭着眼睛都可以找到合适的那种。文章有完整示例代码&#xff0c;拿过来就可以用&#xff0c;欢迎…

洛谷 [传智杯 #5 初赛] B-莲子的机械动力学

题目链接&#xff1a;[传智杯 #5 初赛] B-莲子的机械动力学 题目背景 【题目背景和题目描述的两个题面是完全等价的&#xff0c;您可以选择阅读其中一部分。】 专攻超统一物理学的莲子&#xff0c;对机械结构的运动颇有了解。如下图所示&#xff0c;是一个三进制加法计算器的…

Django 反向解析路由

app2.urls.py from django.urls import path, re_path from . import viewsurlpatterns [path(index, views.index, nameindex),path(url_reverse, views.url_reverse, nameapp2_url_reverse), # 使用reverse()方法反向解析 ,name对于视图的reverse("app2_url_reverse&…

面临挑战:共享WiFi贴项目能否长久存在?

共享WiFi贴项目会长久吗&#xff1f;这个问题是很多创业者想要了解的事情&#xff0c;随着5G网络和共享经济的兴起&#xff0c;共享WiFi贴项目应运而生。在这个信息时代&#xff0c;网络已成为人们日常生活中不可或缺的一部分&#xff0c;而WiFi作为网络的一种接入方式&#xf…

什么是大型语言模型(LLM)?

大型语言模型 (LLM) 是一种能够理解和生成人类语言文本的机器学习模型。它们通过分析大量语言数据集来工作。 一、什么是大型语言模型 (LLM)&#xff1f; 大型语言模型 (LLM) 是一种人工智能 (AI) 程序&#xff0c;它可以识别和生成文本以及完成其他任务。LLM 经过了庞大的数据…

(vue)Module Error (from ./node_modules/eslint-loader/index.js)

(vue)Module Error (from ./node_modules/eslint-loader/index.js) 方法1&#xff1a;直接关闭eslint // vue.config.js module.exports {lintOnSave: false, //关闭eslint语法检查...... }方法2&#xff1a; 参考&#xff1a;解决参考 解决参考&#xff1a;如何修复vue-cli…

Ollama 运行 Cohere 的 command-r 模型

Ollama 运行 Cohere 的 command-r 模型 0. 引言1. 安装 MSYS22. 安装 Golang3. Build Ollama4. 运行 command-r 0. 引言 Command-R Command-R 是一种大型语言模型&#xff0c;针对对话交互和长上下文任务进行了优化。它针对的是“可扩展”类别的模型&#xff0c;这些模型在高…

uniapp+uview 学习笔记(二)—— H5开发

文章目录 前言一、开发步骤1.创建项目2.安装组件库并导入使用3.封装请求4.国际化5.打包 总结 前言 本文主要介绍使用uniapp框架和uview组件库进行H5开发&#xff0c;需要用到的开发工具为HBuilder X。 一、开发步骤 1.创建项目 打开HBuilder X&#xff0c;在顶部栏目选择 新…