【C++】模板进阶 -- 详解

news2025/4/9 13:34:25

一、非类型模板参数

模板参数 类类型形参非类型形参
  • 类型形参,即出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称。
  • 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
namespace bite
{
    // 定义一个模板类型的静态数组
    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. 非类型的模板参数必须在编译期就能确认结果。

二、模板的特化 

1、概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    return left < right;
}

int main()
{
    cout << Less(1, 2) << endl; // 可以比较,结果正确

    Date d1(2023, 9, 29);
    Date d2(2023, 9, 30);
    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、函数模板特化 

函数模板的特化步骤:
  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(2023, 9, 29);
    Date d2(2023, 9, 30);
    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;
}

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


3、类模板特化

(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;
};
偏特化有以下两种表现方式:
  • 部分特化
将模板参数类表中的一部分参数特化。
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
    Data()
    {
        cout << "Data<T1, int>" << endl;
    }
private:
    T1 _d1;
    int _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; 
};

int main()
{
    Data<double, int> d1;      // 调用特化的int版本
    Data<int, double> d2;      // 调用基础的模板 
    Data<int*, int*> d3;       // 调用特化的指针版本
    Data<int&, int&> d4(1, 2); // 调用特化的指针版本

    return 0;
}

(3)类模板特化应用示例

有如下专门用来按照小于比较的类模板 Less:

#include<vector>
#include <algorithm>
template<class T>
struct Less
{
    bool operator()(const T& x, const T& y) const
    {
        return x < y;
    }
};

int main()
{
    Date d1(2023, 9, 27);
    Date d2(2023, 9, 26);
    Date d3(2023, 9, 28);

    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;
}
通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的。但是如果待排序元素是指针,结果就不一定正确。因为:sort 最终按照 Less 模板中方式比较,所以只会比较指针,而不是比较指针指向空间中内容,此时可以使用类版本特化来处理上述问题:
// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
    bool operator()(Date* x, Date* y) const
    {
        return *x < *y;
    }
};

特化之后,在运行上述代码,就可以得到正确的结果。


三、模板分离编译

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;
}
 【分析】


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

四、模板总结 

【优点】

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

【缺陷】

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

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

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

相关文章

基于SSM的毕业生就业管理平台设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

2023年,考PMP用处大吗?

就本身PMP的价值而言&#xff0c;不管到多少年&#xff0c;跟新迭代下&#xff0c;用处都是很大的&#xff0c;就看你会不会用。 PMP会让你学到一套系统的项目管理的流程&#xff0c;还有作为项目管理人士该具备的素质和技能&#xff0c;这就是使得&#xff0c;即便从未接触过…

谷粒商城笔记+踩坑(25)——整合Sentinel实现流控和熔断降级

导航&#xff1a; 【Java笔记踩坑汇总】Java基础进阶JavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线MySQL高级篇设计模式常见面试题源码 SpringCloud基础5——微服务保护、Sentinel 目录 一、Sentinel概述 1.1、服务流控、熔断和降级 1.2、Sentinel 简介…

能否翻译翻译,到底什么才叫“精通Java” ?

01 模糊的岗位能力标准 技术类人员的招聘始终是令HR 与技术面试官头疼的事。 在一般招聘流程中&#xff0c;当确定了某个岗位招聘需求后&#xff0c;技术面试官会与HR 一同商讨并明确该岗位的画像。 明确画像后&#xff0c;一般HR 会负责在招聘平台书写岗位JD&#xff0c;技…

不同商家的订单详情API接口可能会有不同的实现方式,下面是一个通用的订单详情API接口的示

不同商家的订单详情API接口可能会有不同的实现方式&#xff0c;下面是一个通用的订单详情API接口的示例&#xff1a; 请求方式&#xff1a;使用HTTP或HTTPS协议&#xff0c;向指定URL发送GET请求&#xff0c;获取订单详情。 URL格式&#xff1a;商家订单详情API的URL通常由两部…

nginx之基于LNMP搭建论坛

LNMP&#xff1a;企业网站的应用模式之一&#xff0c;早期的论坛架构就是lnmp搭建的 L&#xff1a;Linux平台&#xff0c;操作系统&#xff0c;是另外三个组件的运行平台 N&#xff1a;nginx&#xff0c;提供静态页面 M&#xff1a;mysql&#xff0c;数据库&#xff0c;开元…

【一些理解】搜广推:推荐、广告、搜索算法的区别、入坑?

【一些理解】搜广推&#xff1a;推荐、广告、搜索算法的区别、入坑&#xff1f; 文章目录 【一些理解】搜广推&#xff1a;推荐、广告、搜索算法的区别、入坑&#xff1f;1. 根本区别2. 目标上的区别3. 模型上的区别4. 辅助策略和算法上的区别参考 作为互联网的核心应用“搜广推…

计算机的字符与编码集

文章目录 前言一、字符编码集的历史1.ASCII码2.Extended ASCII码3.字符编码集的国际化 二、中文编码集 前言 今天给大家介绍计算机的字符与编码集&#xff0c;分为两部分&#xff1a;字符编码集的历史、中文编码集。 一、字符编码集的历史 这部分包含三个板块内容&#xff1a…

【特纳斯电子】基于物联网的空气质量检测-实物设计

视频及资料链接&#xff1a;基于物联网的空气质量检测-实物设计 - 电子校园网 (mcude.com) 编号&#xff1a; T0082203M-SW 设计简介&#xff1a; 本设计是基于物联网的空气质量检测系统&#xff0c;主要实现以下功能&#xff1a; 1.通过OLED显示模式、温度、湿度、PM2.5、…

【Java】查找jdk步骤

需求描述 解决方法 第一步 第二步 第三步 第四步 参考文章

自定义jenkins镜像提示FontConfiguration.head错误

系统使用&#xff1a;Debian12&#xff0c;jdk17 提示问题&#xff1a;缺少字体 找一台jdk8的环境&#xff0c;在lib文件夹中找到fontconfig.bfc find / -name *fontconfig* 复制到jenkins目标服务器中&#xff0c;jdk目录的lib中 再次启动jenkins服务正常

云梦富盈:智慧投资引领未来市场

随着2023年的到来&#xff0c;全球股市呈现出令人关注的趋势和挑战。投资者纷纷寻求智慧投资&#xff0c;以更好地把握市场动向。云梦富盈&#xff0c;作为一支备受瞩目的投资团队&#xff0c;正在洞悉并解析2023年全球股市的趋势&#xff0c;为投资者提供智慧投资的护航。 20…

力扣-415.字符串相加

Idea 模拟&#xff1a;竖式加法 从后面往前逐位相加&#xff0c;然后将相加的结果模10&#xff0c;添加到答案字符串中去 最后需要判断一下是否还有进位问题 需要将答案string翻转 AC Code class Solution { public:string addStrings(string num1, string num2) {string ans;…

LruCache实现原理

序、慢慢来才是最快的方法。 回顾 LRU &#xff08;Least Recently Used&#xff09;最近最少策略是最常用的缓存淘汰策略。LRU 策略会记录各个数据块的访问 “时间戳” &#xff0c;最近最久未使用的数据最先被淘汰。与其他几种策略相比&#xff0c;LRU 策略利用了 “局部性…

Sui账户抽象消除用户使用障碍,让大规模用户使用区块链成为可能

Sui通过其本机语言和两个特定功能实现了账户抽象&#xff0c;使账户管理中更加细节化的过程自动化。无论是zkLogin还是赞助交易&#xff0c;都简化了用户的使用过程&#xff0c;而Sui Move的基本结构则使开发人员能够提供丝滑的体验。 最近&#xff0c;随着区块链寻求扩大其用…

Flink(林子雨慕课课程)

文章目录 12.Flink12.1 Flink简介12.2 为什么要选择Flink12.3 Flink应用场景12.4 Flink技术栈、体系架构和编程模型12.5 Flink的安装和编程实战 12.Flink 12.1 Flink简介 企业的处理架构已经由传统数据处理架构和大数据Lamda架构向流处理架构演变 Flink实现了Goole Dataflow…

配置nginx的虚拟主机

1.基于域名的虚拟主机 vim /usr/local/nginx/conf/nginx.conf 复制一个 cd /var/www/html/ mkdir kgc accp cd kgc/ vim index.html this is kgc! cd .. cd accp this is accp! vim /etc/hosts systemctl restart nginx 2.基于ip的虚拟主机 ifconfig ens33:0 192.168…

如何生成SSH服务器的ed25519公钥SHA256指纹

最近搭建ubuntu服务器&#xff0c;远程登录让确认指纹&#xff0c;研究一番搞懂了&#xff0c;记录一下。 1、putty 第一次登录服务器&#xff0c;出现提示&#xff1a; 让确认服务器指纹是否正确。 其中&#xff1a;箭头指向的 ed25519 :是一种非对称加密的签名方法&#xf…

AMEYA360:北京君正集成电路多核异构跨界处理器X2000

• 双XBurst2核&#xff0c;主频1.2GHz • 跨界第三核XBurst0(240MHz)&#xff0c;面向安全管理和实时控制 • H.264编、解码器1080P30fps • 内置LPDDR3 128MB • 双摄Mipi接口双ISP&#xff0c;可实时同步 • 丰富的外设接口 应用领域 • 智能音频&#xff1a;智能音箱&#…

ubuntu安装datasophon问题记录

问题描述: 主机agent分发报红 解决步骤一: 修改datasophon-worker.tar.gz文件 解压/opt/datasophon/DDP/packages目录下的datasophon-worker.tar.gz文件修改datasophon-worker/bin目录下的datasophon-worker.sh文件 . /etc/profile解决步骤二: chkconfig命令不存在 当执行ch…