(学习总结16)C++模版2

news2024/11/18 7:39:06

C++模版2

  • 一、非类型模板参数
  • 二、模板的特化
    • 1. 概念
    • 2. 函数模板特化
    • 3. 类模板特化
      • 全特化
      • 偏特化
      • 类模板特化应用示例
  • 三、模板分离编译
    • 1. 什么是分离编译
    • 2. 模板的分离编译
    • 3. 解决方法
  • 模板总结

以下代码环境为 VS2022 C++。

一、非类型模板参数

模板参数分为类型形参非类型形参

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

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

namespace my
{
	// 定义一个模板类型的静态数组
	template<class T, size_t N = 10>
	class array
	{
	public:

		T& operator[](size_t n)
		{
			return _arr[n];
		}

		const T& operator[](size_t n) const
		{
			return _arr[n];
		}

		size_t size()
		{
			return _size;
		}

		bool empty()
		{
			return _size == 0;
		}

	private:

		T _arr[N];
		size_t _size = N;
	};
}

注意:

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。

  2. 非类型的模板参数必须在编译期就能确认结果。

二、模板的特化

1. 概念

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

#include <iostream>

template<class T>
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	std::cout << Less(1, 2) << std::endl;
	std::cout << Less(1.5, 5.2) << std::endl;

	int b = 2;
	int a = 1;
	std::cout << Less(a, b) << std::endl;

	int* p2 = &b;
	int* p1 = &a;
	std::cout << Less(p1, p2) << std::endl;		// 可以比较,但是结果不一定对

	return 0;
}

可以看到,Less 绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1 指向的 a 显然小于 p2 指向的 b ,但是 Less 内部并没有比较 p1 和 p2 指向的内容,而比较的是 p1 和 p2 指针的地址,这就无法达到预期而错误。

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

2. 函数模板特化

函数模板的特化步骤:

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

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

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

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

#include <iostream>

template<class T>						// 1. 必须要先有一个基础的函数模板
bool Less(T left, T right)
{
	return left < right;
}

// 对 Less 函数模板进行特化
template<>								// 2. 关键字 template 后面接一对空的尖括号 <>
bool Less<int*>(int* left, int* right)	// 3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
{				// 4. 函数形参类型完全对应。
	return *left < *right;
}

int main()
{
	std::cout << Less(1, 2) << std::endl;
	std::cout << Less(1.5, 5.2) << std::endl;

	int b = 2;
	int a = 1;
	std::cout << Less(a, b) << std::endl;

	int* p2 = &b;
	int* p1 = &a;
	std::cout << Less(p1, p2) << std::endl;		// 结果正确

	return 0;
}

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

#include <iostream>

template<class T>						
bool Less(T left, T right)
{
	return left < right;
}

template<>								
bool Less<int*>(int* left, int* right)	// 特化
{				
	std::cout << "Less<int*>(int* left, int* right)" << std::endl;
	return *left < *right;
}

bool Less(int* left, int* right)		// 直接给出
{
	std::cout << "Less(int* left, int* right)" << std::endl;
	return *left < *right;
}

int main()
{
	int b = 2;
	int a = 1;
	int* p2 = &b;
	int* p1 = &a;

	// 函数模板特化与直接给出的函数都存在,则优先调用直接给出的函数
	std::cout << Less(p1, p2) << std::endl;		

	// 这个是指定调用模板 Less 函数
	std::cout << Less<int*>(p1, p2) << std::endl;

	return 0;
}

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

3. 类模板特化

全特化

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

#include <iostream>

template<class T1, class T2>
class Data						// 1. 类模板存在
{
public:

	Data()
	{
		std::cout << "Data<T1, T2>" << std::endl;
	}

private:

	T1 _d1;
	T2 _d2;
};

template<>						// 2. 特化时 template 后只带 <>
class Data<int, double>			// 3. Date 后带 <> , <> 里带特化的类型
{		// 4. 参数类型对应
public:

	Data()
	{
		std::cout << "Data<int, double>" << std::endl;
	}

private:

	int _d1;
	double _d2;
};

int main()
{
	Data<int, int> d1;
	Data<int, double> d2;

	return 0;
}

偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:

template<class T1, class T2>
class Data						
{
public:

	Data()
	{
		std::cout << "Data<T1, T2>" << std::endl;
	}

private:

	T1 _d1;
	T2 _d2;
};

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

  1. 部分特化:将模板参数类表中的一部分参数特化。
template<class T1>
class Data<T1, double>
{
public:

	Data()
	{
		std::cout << "Data<T1, double>" << std::endl;
	}

private:

	T1 _d1;
	double _d2;
};
  1. 参数更进一步的限制:偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
template<class T1, class T2>
class Data<T1*, T2*>			// 两个参数偏特化为指针类型
{
public:

	Data()
	{
		std::cout << "Data<T1*, T2*>" << std::endl;
	}

private:

	T1* _d1;
	T2* _d2;
};

template<class T1, class T2>
class Data<T1&, T2&>			// 两个参数偏特化为引用类型
{
public:

	Data()
	{
		std::cout << "Data<T1&, T2&>" << std::endl;
	}

private:

	const T1& _d1 = 1;
	const T2& _d2 = 2;
};

void test()
{
	Data<int, int> d1;			// 调用基础的类模板
	Data<double, double> d2;	// 调用特化的 double 版本
	Data<int*, int*> d3;		// 调用特化的指针版本
	Data<int&, int&> d4;		// 调用特化的引用版本
}

类模板特化应用示例

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

#include <iostream>
#include <algorithm>
#include <vector>

template<class T>
class Less
{
public:

	bool operator()(const T& left, const T& right) const
	{
		return left > right;
	}
};

void test()
{
	int c = 7;
	int a = 5;
	int b = 6;

	std::vector<int> arr1;
	arr1.push_back(a);
	arr1.push_back(b);
	arr1.push_back(c);

	sort(arr1.begin(), arr1.end(), Less<int>());

	for (auto e : arr1)
	{
		std::cout << e << " ";
	}
	std::cout << std::endl;

	std::vector<int*> arr2;
	arr2.push_back(&a);
	arr2.push_back(&b);
	arr2.push_back(&c);

	sort(arr2.begin(), arr2.end(), Less<int*>());

	for (auto e : arr2)
	{
		std::cout << *e << " ";
	}
	std::cout << std::endl;
}

int main()
{
	test();

	return 0;
}

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

template<>
class Less<int*>
{
public:

	bool operator()(int* left, int* right) const
	{
		return *left > *right;
	}
};

特化之后,在运行上述代码,就可以得到正确的结果。当然自定义的类型也可以,并且使用指针作为参数进行排序,可以节省占用字节多的类型的空间。

三、模板分离编译

1. 什么是分离编译

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

2. 模板的分离编译

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

#include <iostream>

// add.h
template<class T>
T add(const T& left, const T& right);

// add.cpp
// #include "add.h"
template<class T>
T add(const T& left, const T& right)
{
	return left + right;
}

// main.cpp
// #include "add.h"
int main()
{
	add(1, 2);

	return 0;
}

在这里插入图片描述

3. 解决方法

解决方法:

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者 “xxx.h” 其实是可以的。推荐使用这种。

  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

模板总结

优点:

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

缺陷:

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

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

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

相关文章

硬件实用技巧:螺丝M标准、螺丝长度以及螺帽M直径

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/142205318 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

[LWIP] 如何实现LWIP热插拔功能

LWIP 1.4.1 硬件stm32f429 HAL 1.8.1 UCOSIII 步骤如下 1、在lwip的opt.h中使能宏定义LWIP_NETIF_LINK_CALLBACK 链接发生变化时调用回调函数 2、在初始化结束后调用函数netif_set_link_callback&#xff0c;为网卡设置一个回调函数&#xff0c;回调参考代码如下 /*** bri…

【诉讼流程-健身房-违约-私教课-多次沟通无效-民事诉讼-自我学习-铺平通往法律的阶梯-讲解(1)】

【诉讼流程-健身房-违约-私教课-多次沟通无效-前期法律流程-民事诉讼-自我学习-铺平通往法律的阶梯-讲解&#xff08;1&#xff09;】 &#xff08;1&#xff09;前言说明1、目的2、说明1- 本章你将会看到的内容2 - 健身房其中一些套路1、先给予体验课。2、合同里的文字陷阱3、…

【DOA估计】一种基于高阶广义奇异值分解的多声源方向估计方法【附MATLAB代码】

微信公众号&#xff1a;EW Frontier QQ交流群&#xff1a;554073254 摘要 一种基于高阶广义奇异值分解的多声源方位估计方法本文提出了一种有效的宽带声源波达方向&#xff08;DOA&#xff09;估计方法。所提出的框架提供了一种有效的方法来构建一个宽带互相关矩阵从多个窄带互…

压测服务器并使用 Grafana 进行可视化

简介 仓库代码 GitCode - 全球开发者的开源社区,开源代码托管平台 参考 Welcome! - The Apache HTTP Server Project Grafana | 查询、可视化、警报观测平台 https://prometheus.io/docs/introduction/overview/

html+css+js网页设计 旅游 厦门旅游网10个页面

htmlcssjs网页设计 旅游 厦门旅游网10个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&am…

基于vue框架的宠物寄养系统3d388(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,宠物信息,宠物分类,寄养店,宠物寄养,宠物领养,家庭环境,用户宠物 开题报告内容 基于Vue框架的宠物寄养系统开题报告 一、引言 随着人们生活水平的提高和宠物文化的普及&#xff0c;宠物已成为许多家庭不可或缺的一员。因此&…

windows和linux安装mysql5.7.31保姆级教程

一&#xff0c;资源如下&#xff0c;里面有windows和linux版的安装软件&#xff0c;内含Visual C2013中文版windows系统插件 windows资源地址&#xff1a;https://download.csdn.net/download/l1o3v1e4ding/89725150 linux&#xff08;centos&#xff09;资源地址&#xff1a;…

线性基大发现

一.构造方法 1.贪心法&#xff08;每一个数往里插入即可&#xff09; /*贪心法构造线性基的特点&#xff1a; 1.从小到大排列 2.各个基的高位可能存在重复的1 2.线性基不是唯一的&#xff0c;与原集合的元素顺序有关*/ void insert(int x){//贪心法for(int i63;i>0;i--){i…

“百度热搜”揭示月饼遇冷背后:如何在经济下行中理性消费 + 应对风险?

中秋将至,月饼本该成为节日的主角。然而,令人惊讶的是,近期的“百度热搜”显示月饼销售出现了前所未有的冷遇。这背后的原因是什么?在经济下行的背景下,人们的消费观念正悄然转变。今天,我们就来聊聊如何在这样的经济环境中保持理性消费,并应对潜在的经济风险。 经济下行…

数组去重、数组扁平化

数组去重 排序然后for循环判断相邻的两个是否一样 定义新数组&#xff0c;for循环新数组中没有这个元素就添加 利用对象的键&#xff0c;需要新建对象和数组 利用双重for循环判断 利用for循环和indexOf判断是否存在 利用newSet构造函数不接受重复数据 数组扁平化

visual studio code下载教程(手把手)

今天我来给大家介绍一下visual studio code (VScode)的下载 一、VSCode介绍 VSCode 是一款由微软开发且跨平台的免费源代码编辑器&#xff1b;该软件支持语法高亮、代码自动补全、代码重构、查看定义功能&#xff0c;并且内置了命令行工具和 Git 版本控制系统。 二、官方下载…

谷歌将把那些冗长的文档变成你下一个最喜欢的播客

如果你有很多学校或工作的阅读任务&#xff0c;但更喜欢听播客&#xff0c;谷歌全新的AI驱动的Audio Overview工具可以满足你的需求。这项工具首次在今年的Google I/O大会上展示&#xff0c;Audio Overviews可以让你将文档、幻灯片和其他文本转换为一个AI主持的音频节目&#x…

深度学习:入门简介

目录 一、深度学习与机器学习的关系 二、神经网络构造 三、推导 四、感知器与多层感知器 1.感知器 2.多层感知器 3.偏置 五、如何确定输入层和输出层个数 一、深度学习与机器学习的关系 深度学习是一种机器学习的子领域&#xff0c;利用多层神经网络来学习数据的复杂特…

Windows技术栈企业基础底座(1)-为基于Windows的Nginx安装证书

企业的基础环境是一个组织的信息化数字化底座。传统企业基础环境多种系统&#xff0c;应用交杂&#xff0c;多种技术栈使得深入运维成本极大&#xff0c;且人员知识技能较难复用&#xff0c;造成资源浪费。本系列旨在尝试推动这一理念, 建立Windows, 或linux聚焦的技术栈的企业…

部署自己的对话大模型,使用Ollama + Qwen2 +FastGPT 实现

部署资源 AUTODL 使用最小3080 资源&#xff0c;cuda > 12.0使用云服务器&#xff0c;部署fastGPT oneAPI&#xff0c;M3E 模型 操作步骤 配置代理 export HF_ENDPOINThttps://hf-mirror.com下载qwen2模型 huggingface-cli download Qwen/Qwen2-7B-Instruct-GGUF qwen2-7…

Flutter能赚钱,你还担心它被Google抛弃吗?

哈喽&#xff0c;我是老刘 Flutter自从诞生以来有很多的质疑和担心。 其中Flutter会不会被Google放弃是大家最重要的一个担心。 尤其是前段时间Flutter团队裁员后这种担心达到了顶点。 但是由LeanCode主导的一次针对Flutter的技术调查报告&#xff0c;应该能很大程度上解答这个…

为什么要分库分表

目录 为什么分库分表业务驱动分库分表优缺点优点缺点 如何分库分表分库分表原则分库多少合适分表多少合适分库分表字段选择逻辑 库或表不够怎么办数据归档 为什么分库分表 业务驱动 业务&#xff1a;增长快&#xff0c;业务复杂度高。系统流量疯狂增长&#xff0c;部分大表数…

[linux 驱动]i2c总线设备驱动详解与实战

目录 1 描述 2 结构体 2.1 bus_type 2.2 i2c_bus_type 2.2.1 i2c_device_match 2.2.2 i2c_device_probe 2.2.3 i2c_device_remove 2.2.4 i2c_device_shutdown 2.2 i2c_adapter 2.3 i2c_algorithm 2.4 i2c_driver 2.5 i2c_client 3 i2c核心 3.1 注册i2c适配器 3.2…

windows电脑怎么录屏?电脑录屏全攻略,轻松捕捉精彩瞬间

在数字化时代&#xff0c;屏幕录制已成为我们日常生活和工作中不可或缺的一部分。无论是记录游戏的高光时刻、制作教学视频&#xff0c;还是保存重要的在线会议内容&#xff0c;Windows电脑都为我们提供了多种高效便捷的录屏方式&#xff0c;如果你还不知道怎么录屏&#xff0c…