模板(二)

文章目录

  • 模板(二)
    • 1 非类型模板参数
    • 2. 模板的特化
      • 2.1. 概念
      • 2.2 函数模板特化
      • 2.3 类模板特化
        • 2.3.1 全特化
        • 2.3.2 偏特化
        • 2.3.3 类模板特化应用示例
    • 3 模板的分离编译
      • 3.1 什么是分离编译
      • 3.2 模板的分离编译
      • 3.3 解决方法
    • 4. 模板总结

模板(二)

1 非类型模板参数

模板参数分类:

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

定义一个 模板类型 的静态数组:

#include <iostream>
using namespace std;

#define N 10
template <class T>
class array
{
public:
    //...
private:
    T _a[N];
};
int main()
{
    array<int> a1;
    array<double> a2;
    return 0;
}

通过官方文档查看array的定义:

在这里插入图片描述

此时,根据文档来定义一个 非类型模板

#include <iostream>
using namespace std;

template <class T, size_t N>
class array
{
public:
    //...
private:
    T _a[N];
};
int main()
{
    array<int, 10> a1;
    array<double, 20> a2;
    return 0;
}

通过调试的检测窗口观察创建的对象:

在这里插入图片描述

注意:

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

2. 模板的特化

2.1. 概念

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

比如:实现了一个专门用来进行小于比较的函数模板

#include <iostream>
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)
    {
        _cout << d._year << "-" << d._month << "-" << d._day;
        return _cout;
    }

private:
    int _year;
    int _month;
    int _day;
};

// 函数模板 -- 参数匹配
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指针的地址,这就无法达到预期而错误。此时,就需要对模板进行特化。

为了修正这个结果的错误,可以使用模板的特化来解决:

#include <iostream>
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)
    {
        _cout << d._year << "-" << d._month << "-" << d._day;
        return _cout;
    }

private:
    int _year;
    int _month;
    int _day;
};

// 函数模板 -- 参数匹配
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;
}

使用模板特化Date*之后,当对象是指针时,就走特化这个函数,如果不是指针,就走模板类型.

运行结果:

在这里插入图片描述

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

2.2 函数模板特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板

  2. 关键字template后面接一对空的尖括号<>

  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

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

//特化
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

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

2.3 类模板特化

2.3.1 全特化

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

#include <iostream>
using namespace std;

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

int main()
{
    Data<int, int> d1;
    Data<int, char> d2;
    return 0;
}

观察实例化对象结果:

在这里插入图片描述

2.3.2 偏特化

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

比如对于以下模板类:

template <class T1, class T2>
struct Data
{
    Data() 
    { 
        cout << "Data<T1, T2>" << endl; 
    }
};

偏特化有以下两种表现方式:

  • 部分特化

    将模板参数类表中的一部分参数特化

    #include <iostream>
    using namespace std;
    
    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;
    };
    
    int main()
    {
        Data<double, double> d1;  //模板  
        Data<double, int> d2;  //特化
        return 0;
    }
    

    输出结果:

    在这里插入图片描述

  • 参数更进一步的限制

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

#include <iostream>
using namespace std;

template <class T1, class T2>  //主模板
struct Data
{
    Data()
    {
        cout << "Data<T1, T2>" << endl;
    }
};

// 两个参数偏特化为指针类型
template <class T1, class T2>
class Data<T1 *, T2 *>
{
public:
    Data() { cout << "Data<T1*, T2*>" << endl; }

private:
    T1 _d1;
    T2 _d2;
};

// 两个参数偏特化为引用类型
template <class T1, class 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;
}

​ 特化调用结果:

在这里插入图片描述

2.3.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(2022, 7, 7);
    Date d2(2022, 7, 6);
    Date d3(2022, 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;
}

通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的。但是如果待排序元素是指针,结果就不一定正确。因为:sort最终按照Less模板中方式比较,所以只会比较指针,而不是比较指针指向空间中内容,此时可以使用类版本特化来处理上述问题:

// 对Less类模板按照指针方式特化
template <>
struct Less<Date *>
{
    bool operator()(Date *x, Date *y) const
    {
        return *x < *y;
    }
};

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

3 模板的分离编译

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. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

【分离编译扩展阅读】

4. 模板总结

【优点】

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

  2. 增强了代码的灵活性

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长

  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

调试 WebSocket API 技巧分享

WebSocket 是一种在单个 TCP 连接上实现全双工通信的先进 API 技术。与传统的 HTTP 请求相比&#xff0c;WebSocket 提供了更低的延迟和更高的通信效率&#xff0c;使其成为在线游戏、实时聊天等应用的理想选择。 开始使用 Apifox 的 WebSocket 功能 首先&#xff0c;在项目界…

成都直播产业园「天府锋巢」电商流量深度变现,助力企业降本增效

天府锋巢园区环境 天府锋巢直播基地 其他重点特色产业服务 等您来解锁&#xff01; 「锋巢资讯 聚焦天府 诚邀企业 敬请关注」

Flink面试(1)

1.Flink 的并行度的怎么设置的&#xff1f; Flink设置并行度的几种方式 1.代码中设置setParallelism() 全局设置&#xff1a; 1 env.setParallelism(3);  算子设置&#xff08;部分设置&#xff09;&#xff1a; 1 sum(1).setParallelism(3) 2.客户端CLI设置&#xff0…

C++设计模式:中介者模式(十五)

1、定义与动机 定义&#xff1a;用一个中介对象来封装&#xff08;封装变化&#xff09;一系列的对象交互。中介者使各个对象不需要显示的相互引用&#xff08;编译时依赖 -> 运行时依赖&#xff09;&#xff0c;从而使其耦合松散&#xff08;管理变化&#xff09;&#xff…

C#带引导窗体的窗体设计方法:创建特殊窗体

目录 1.设计操作流程 2.实例 &#xff08;1&#xff09;Resources.Designer.cs &#xff08;2&#xff09;Frm_Main.Designer.cs &#xff08;3&#xff09;Frm_Main.cs &#xff08;4&#xff09;Frm_Start.Designer.cs &#xff08;5&#xff09;Frm_Start.cs &#…

HashData获得华为鲲鹏Validated认证 信创版图持续壮大

近日&#xff0c;经过一系列严格测试评估&#xff0c;酷克数据自研企业级HashData云数仓通过华为鲲鹏高阶调优认证&#xff0c;成功获得鲲鹏Validated技术认证书。 在本次Validated认证过程中&#xff0c;酷克数据携手北京鲲鹏联合创新中心&#xff0c;针对数据仓库的典型应用…

微信小程序实现支付功能——微信jsapi支付

微信小程序jsapi支付的实现涉及多个步骤&#xff0c;主要包括前端请求订单信息、后端处理订单并返回支付参数、前端调用jsapi进行支付等。以下是一个基本的实现流程&#xff1a; 1. 前端请求订单信息 在小程序中&#xff0c;当用户触发支付行为时&#xff08;例如点击购买按钮…

Vue之v-on事件修饰符的含义及使用

背景&#xff1a;Vue 拆封了一个组件&#xff0c;在组件里面会使用一个方法来改变父组件传过来的值&#xff0c; 但是在子组件里面操作父组件的数据变更&#xff0c;实在比较麻烦&#xff08;因为单向数据流&#xff09;&#xff0c; So 能不能直接在组件上面绑定事件方法呢&…

vue项目打包时因为图片问题报错

执行 npm run build命令打包项目时报错&#xff0c;看起来是图片的问题&#xff1a; package.json里面image-webpack-loader的版本是^7.0.1 解决方案&#xff1a; 1、先卸载 npm uninstall image-webpack-loader 2、用cnpm重新安装 cnpm install image-webpack-loader --save…

『FPGA通信接口』串行通信接口-IIC(2)EEPROM读写控制器

文章目录 1.EEPROM简介2.AT24C04简介3.逻辑框架设计4.随机读写时序5.仿真代码与仿真结果分析6.注意事项7.效果8.传送门 1.EEPROM简介 EEPROM (Electrically Erasable Programmable read only memory) 是指带电可擦可编程只读存储器。是一种掉电后数据不丢失的存储芯片。在嵌入…

计算机视觉 CV 八股分享 [自用](更新中......)

目录 一、深度学习中解决过拟合方法 二、深度学习中解决欠拟合方法 三、梯度消失和梯度爆炸 解决梯度消失的方法 解决梯度爆炸的方法 四、神经网络权重初始化方法 五、梯度下降法 六、BatchNorm 七、归一化方法 八、卷积 九、池化 十、激活函数 十一、预训练 十二…

【Python-Pygame】

Python-Pygame ■ Pygame-简介■ Pygame-安装■ Pygame-Rect区域位置■ Pygame-Draw绘图函数■ Pygame-■ Pygame-■ Pygame-■ Pygame-事件监听■ Pygame-Event事件模块■ Pygame-游戏循环■ Pygame-Display显示模块■ Pygame-Time时间控制■ Pygame-Font文本和字体■ Pygame-…

2024企业数字化转型资料包(10G,1000+份,数字化转型有这份就够了!!!)

2022新时代数字政府建设与发展若干思考-119页.pptx 2023产业数字化建设方案-51页.pptx 2023人工智能与数字化转型的业财融合.pptx 2023企业数字化转型大数据湖一体化平台项目建设方案.pptx 2023供应链数字化转型顶层架构设计方案-37页.pptx 2023国企智改数转之道解决方案.pptx …

QT客户端开发的应用场景

QT 是一跨平台应用程序开发框架&#xff0c;支持多种操作系统&#xff0c;包括 Windows、macOS、Linux、Android、iOS 和嵌入式系统等。这使得 QT 非常适合开发需要在多种平台上运行的应用程序。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交…

花房短剧小程序视频怎么下载到本地#下载高手

本文就教你如何使用下载高手下载花房短剧小程序 本文用到的工具我已经给大家打包好了,有需要的自己取一下 链接&#xff1a;https://pan.baidu.com/s/1KtR6830x8GciKtNcSRhQMg?pwd1234 提取码&#xff1a;1234 --来自百度网盘超级会员V10的分享 1.首先退出微信,记得必须右…

【LeetCode刷题记录】21. 合并两个有序链表

21 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 …

AI大模型探索之路-资料篇:大模型开发相关地址信息收藏

文章目录 前言一、OpenAI大模型二、LangChain开发框架三、RAGA评估框架四、GLM大模型五、搜索服务1. Tavily Search API 六、文本LLM大模型七、多模态LLM模型八、模型排行榜1.大模型评测体系&#xff08;司南OpenCompass&#xff09;2.大模型排行榜&#xff08;DataLearner AI&…

校园能源消耗监测管理系统,为您提供节能减排方案

现如今 &#xff0c;在全球加快推动能源转型、减少碳排的背景下&#xff0c;节能减排已成为各行各业的共同诉求。作为最具示范效应的教育机构&#xff0c;学校在节能减排领域引领着重要的作用。 学效能源消耗监测管理系统是一套涵盖、教学楼、办公楼、图书馆、学生公寓、体育场…

SpringBoot+layuimini实现左侧菜单动态展示

layuimini左侧菜单动态显示 首先我们看一下layuimini的原有菜单显示格式 {"homeInfo": {"title": "首页","href": "page/welcome-2.html?t2"},"logoInfo": {"title": "LAYUI MINI","…

如何加盟共享wifi项目?了解套路有哪些?

自共享wifi项目推出在市场火爆后&#xff0c;各路资本都看到了该项目的广阔前景&#xff0c;纷纷开始研发程序&#xff0c;想要趁机分一杯羹。但对于普通人而言&#xff0c;独立研发程序显然不大现实&#xff0c;于是&#xff0c;共享wifi项目如何加盟便成为了绝大多数人最为关…