“编写一次,无限应用:深入理解C++模板“

news2024/11/27 4:22:19

在这里插入图片描述

🚀write in front🚀
📜所属专栏: C++学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

文章目录

  • 前言
  • 一.静态对象和类型的混淆:
  • 二.模板的特化:
    • 1.函数模板特化:
    • 2.类模板特化:
      • 全特化:
      • 偏特化:
        • a.特化部分参数:
        • b.参数的进一步修饰:
      • 具体使用场景:
  • 三.模板分离编译
    • 3.1 什么是分离编译
    • 3.2 模板的分离编译
    • 3.3 解决方法
  • 总结模板的优缺点:

前言

  在了解完模板初阶以后,我们来学习进阶的知识!

一.静态对象和类型的混淆:

  我们先来看看这个代码:

template<class Container>
void print(const Container &v)
{
	Container::const_iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

void test1()
{
vector<int> vv;
print(vv);
}

  这段代码在运行的时候会出错,可是为什么呢?

我们在类里面用::访问的时候有两种情况:

  1. 静态成员对象
  2. 类型

  编译器不知道这里的访问是静态对象还是类型名,所以这里就会报错。

那么怎么解决呢?

  我们只要在类型名之前加一个typename或者直接用auto来表示类型就可以了。

  所以,在类模板或者函数模板中,当你引用模板参数的嵌套类型时,最好在前面加上 typename,这样可以消除歧义,使代码更具可读性,也有助于编译器正确地解析代码。

二.模板的特化:

  通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板:

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

  如果我们通过指针比大小,那肯定是错误的。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化类模板特化

1.函数模板特化:

  我们先来看看一个比较大小的函数

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}

  如果此时我们通过指针想要比较他们的大小时,就可在这个函数的基础上特化出一个新模板:

template<>
bool Less<int*>(int* x, int * y)
{
	return *x < *y;
}

  但是对于函数模板的特化,我们可以直接重新写一个,就没必要在特化一个新的了。
  当然,我们也可以通过胁制类型来特化一个模板函数:

template<class T>
bool Less(T* x, T* y)
{
	return *x < *y;
}

重点

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表:必须要和模板函数的基础参数类型完全相同(别加const就可以了)如果不同编译器可能会报一些奇怪的错误。

2.类模板特化:

  类模板特化分为两种:全特化和偏特化

全特化:

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

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 _
};
void TestVector()
{
Data<int, int> d1;
Data<int, char> d2;
}

  通过运行发现,如果有符合的全特化的类,那么就不用在通过模板实例化一份类出来。

偏特化:

  偏特化也有两种情况:

a.特化部分参数:

template<class T1>
class Data<T1, double>
{
public:
	Data() { cout << "Data<T1, double>" << endl; }
private:
};

  这样的偏特化,就将第二个参数定死为double类型,只要第二个类型为double,而且没有更具体的类,我们就会调用这个类。

b.参数的进一步修饰:

template<class T1, class T2>
class Data<T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
private:
};

template<class T1, class T2>
class Data<T1&, T2&>
{
public:
	Data() { cout << "Data<T1&, T2&>" << endl; }
private:
};

  在类后面对参数进行限制,第一个偏特化限制为指针,第二个偏特化限制为引用。

具体使用场景:

  我们通过算法库里的sort函数排序来调用仿函数的例子来使用一下类模板的特化:

#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.1 什么是分离编译

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

3.2 模板的分离编译

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

// a.h
template<class T>
T Add(const T& left, const T& right);
void func1(int a,int b)void func2(double a,double b)// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
void func1(int a,int b)
{
return a+b;
}
void func2(double a,double b)
{
return a+b;
}
// main.cpp
#include"a.h"
int main()
{
func1(1,2);
func2(1.0,2.0);
Add("abc","def");
return 0;
}

  在分析前我们先来回顾一下编译链接的相关知识:
在这里插入图片描述
在这里插入图片描述

在编译运行时我们会发现:

  • func1链接查到了
  • func2链接查找不到,因为只有声明没有定义
  • Add链接查找不到,但是我们定义了呀

  那么为什么会出现这样的情况呢?其实啊,我们发现func1可以编译成功,是因为他是一段“具体”的函数,而我们知道,模板实例化是在编译的时候进行的,Add函数在编译的时候只是说可以在头文件找到他的声明,但是并没有对这个模板实例化。所以在链接的时候我们不能找到实例化的函数地址与之对应,链接就会报错。

3.3 解决方法

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。
  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用:
    比如在刚刚的a.cpp文件下加一个:
template
Add<string>;

总结模板的优缺点:

【优点】

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

【缺陷】

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

  更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
C语言学习
算法
智力题
初阶数据结构
Linux学习
C++学习
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

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

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

相关文章

E8—Aurora 64/66B ip实现GTX与GTY的40G通信2023-08-12

1. 场景 要在贴有K7系列FPGA芯片的板子和贴有KU系列FPGA芯片的板子之间通过光模块光纤QSFP实现40G的高速通信。可以选择的方式有多种&#xff0c;但本质的方案就一种&#xff0c;即实现4路GTX与GTY之间的通信。可以选择8B/10B编码通过GT IP核实现&#xff0c;而不能通过Aurora…

深度学习(36)—— 图神经网络GNN(1)

深度学习&#xff08;36&#xff09;—— 图神经网络GNN&#xff08;1&#xff09; 这个系列的所有代码我都会放在git上&#xff0c;欢迎造访 文章目录 深度学习&#xff08;36&#xff09;—— 图神经网络GNN&#xff08;1&#xff09;1. 基础知识2.使用场景3. 图卷积神经网…

基于kubeasz部署高可用k8s集群

在部署高可用k8s之前&#xff0c;我们先来说一说单master架构和多master架构&#xff0c;以及多master架构中各组件工作逻辑 k8s单master架构 提示&#xff1a;这种单master节点的架构&#xff0c;通常只用于测试环境&#xff0c;生产环境绝对不允许&#xff1b;这是因为k8s集群…

C++学习笔记——从面试题出发学习C++

C学习笔记——从面试题出发学习C C学习笔记——从面试题出发学习C1. 成员函数的重写、重载和隐藏的区别&#xff1f;2. 构造函数可以是虚函数吗&#xff1f;内联函数可以是虚函数吗&#xff1f;析构函数为什么一定要是虚函数&#xff1f;3. 解释左值/右值、左值/右值引用、std:…

探索FSM (有限状态机)应用

有限状态机&#xff08;FSM&#xff09; 是计算机科学中的一种数学模型&#xff0c;可用于表示和控制系统的行为。它由一组状态以及定义在这些状态上的转换函数组成。FSM 被广泛用于计算机程序中的状态机制。 有限状态机&#xff08;FSM&#xff09;应用场景 在各种自动化系统…

LVGL学习笔记 29 - LED

目录 1. 设置颜色 2. 设置OFF颜色 3. 设置对比度 4. 改变状态 功能类似CheckBox&#xff0c;用一个方形或则圆形的控件显示开关状态。 lv_obj_t* led1 lv_led_create(lv_scr_act());lv_obj_t* led2 lv_led_create(lv_scr_act());lv_obj_align(led1, LV_ALIGN_CENTER, -80…

生产执行MES系统:提升企业灵活性和响应速度的关键利器

在竞争激烈的市场环境下&#xff0c;企业需要不断提高其灵活性和响应速度&#xff0c;以适应快速变化的需求和市场动态。生产执行MES&#xff08;Manufacturing Execution System&#xff09;系统作为信息技术的重要应用&#xff0c;为企业提供了强大的工具和平台&#xff0c;能…

redis 数据结构(一)

Redis 为什么那么快 redis是一种内存数据库&#xff0c;所有的操作都是在内存中进行的&#xff0c;还有一种重要原因是&#xff1a;它的数据结构的设计对数据进行增删查改操作很高效。 redis的数据结构是什么 redis数据结构是对redis键值对值的数据类型的底层的实现&#xff0c…

金蝶云星空与金蝶云星空对接集成采购入库查询连通采购入库新增(MW_写入测试)

金蝶云星空与金蝶云星空对接集成采购入库查询连通采购入库新增(MW_写入测试) 对接源平台:金蝶云星空 金蝶K/3Cloud在总结百万家客户管理最佳实践的基础上&#xff0c;提供了标准的管理模式&#xff1b;通过标准的业务架构&#xff1a;多会计准则、多币别、多地点、多组织、多税…

YOLOv5基础知识入门(3)— 目标检测相关知识点

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。YOLO算法发展历程和YOLOv5核心基础知识学习完成之后&#xff0c;接下来我们就需要学习目标检测相关知识了。为了让大家后面可以顺利地用YOLOv5进行目标检测实战&#xff0c;本节课就带领大家学习一下目标检测的基础知识点&…

运放电路笔记5-其它典型电路

电容的特性——“通交流&#xff0c;隔直流” 电容器可以用来对交流信号进行通路&#xff0c;同时隔离直流信号。这是因为电容器对交流信号具有低阻抗&#xff08;通过&#xff09;和对直流信号具有高阻抗&#xff08;阻断&#xff09;的特性。 在低频情况下&#xff0c;电容器…

Layui精简版,快速入门

目录 LayUI之入门 1.什么是layui 2.layui入门 3.自定义模块 4.用户登录 5.主页搭建 LayUI之动态树 main.jsp main.js LayUI之动态选项卡 1.选项卡 main.jsp main.js 2.用户登录 User.java UserDao.java UserAction.java R.java LayUI之用户管理 1.用户查询…

填补封闭社区一加ACE2V在151版本下安装KernelSU方式获取ROOT

背景需求,Android移动端软件太过流氓,随意驻留后台,其他root方案不满意 第一步,请将手机升级到你想稳定的版本 参考文档, 安装 | KernelSU https://kernelsu.org/zh_CN/guide/installation.html#patch-boot-image 免github下载地址 KernelSU https://mrzzoxo.lanzoub.com/b…

设计模式之工厂方法模式(FactoryMethod)

一、概述 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。FactoryMethod使一个类的实例化延迟到其子类。 二、适用性 1.当一个类不知道它所必须创建的对象的类的时候。 2.当一个类希望由它的子类来指定它所创建的对象的时候。 3.当类将创建对象的职责委…

[ubuntu]创建root权限的用户 该用户登录后自动切换为root用户

一、创建新用户 1、创建新用户 sudo useradd -r -m -s /bin/bash 用户名 # -r&#xff1a;建立系统账号 -m&#xff1a;自动建立用户的登入目录 -s&#xff1a;指定用户登入后所使用的shell2、手动为用户设置密码 passwd 用户名 二、为用户增加root权限 1、添加写权限 ch…

DIP: NAS(Neural Architecture Search)论文阅读与总结(双份快乐)

文章地址: NAS-DIP: Learning Deep Image Prior with Neural Architecture SearchNeural Architecture Search for Deep Image Prior 参考博客:https://zhuanlan.zhihu.com/p/599390720 文章目录 NAS-DIP: Learning Deep Image Prior with Neural Architecture Search1. 方法…

ChatGPT等人工智能编写文章的内容今后将成为常态

BuzzFeed股价上涨200%可能标志着“转向人工智能”媒体趋势的开始。 周四&#xff0c;一份内部备忘录被华尔街日报透露BuzzFeed正计划使用ChatGPT聊天机器人-风格文本合成技术来自OpenAI&#xff0c;用于创建个性化盘问和将来可能的其他内容。消息传出后&#xff0c;BuzzFeed的…

SpringBoot 3自带的 HTTP 客户端工具

原理 Spring的HTTP 服务接口是一个带有HttpExchange方法的 Java 接口&#xff0c;它支持的支持的注解类型有&#xff1a; HttpExchange&#xff1a;是用于指定 HTTP 端点的通用注释。在接口级别使用时&#xff0c;它适用于所有方法。GetExchange&#xff1a;为 HTTP GET请求指…

题解:ABC276E - Round Trip

题解&#xff1a;ABC276E - Round Trip 题目 链接&#xff1a;Atcoder。 链接&#xff1a;洛谷。 难度 算法难度&#xff1a;普及。 思维难度&#xff1a;提高。 调码难度&#xff1a;提高。 综合评价&#xff1a;困难。 算法 bfs。 思路 从起点周围四个点中任选两…

jenkins自动化构建保姆级教程(持续更新中)

1.安装 1.1版本说明 访问jenkins官网 https://www.jenkins.io/&#xff0c;进入到首页 点击【Download】按钮进入到jenkins下载界面 左侧显示的是最新的长期支持版本&#xff0c;右侧显示的是最新的可测试版本&#xff08;可能不稳定&#xff09;&#xff0c;建议使用最新的…