C++ -- 模板进阶

非模板类型参数

        模板参数分为类型形参与非类型形参。类型形参:出现在模板参数列表中,跟在class 或 typename之类的参数类型名称。非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中将该参数当成常量来使用。如果我们想定义一个静态数组的类模板,就可以使用非类型形参,示例如下:

namespace zy
{
	//定义一个模板类型的静态数组
	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. 非类型的模板参数必须在编译期间就能确认结果。

模板的特化

概念

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

class Date
{
public:
	//全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator<(const Date& d);

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指针的地址,这就无法达到预期的内容比较。

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

函数模板特化

        函数模板的特化步骤:

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

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

bool Less(Date* left, Date* right)
{
return *left < *right;
}

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

类模板特化

全特化

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

// 类模板
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;
}

int main()
{
	TestVector();
	return 0;
}

偏特化

        偏特化 :对模板部分参数进行特化或对参数进一步进行条件限制设计的版本。示例如下:

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;
};
void test2()
{
	Data<double, int> d1; // 调用特化的int版本
	Data<int, double> d2; // 调用基础的模板
	Data<int*, int*> d3; // 调用特化的指针版本
	Data<int&, int&> d4(1, 2); // 调用特化的引用版本
}

类模板特化应用实例

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

模板分离编译模式

介绍

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

过程

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

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

 链接错误的解决方法:

  1. 将声明和定义放到一个文件“.h”中,这种比较推荐。
  2. 模板定义的位置处显示实例化。这种方法不实用,不推荐使用。

扩展阅读:为什么C++编译器不能支持对模板的分离式编译


模板总结:

优点:

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

缺点:

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

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

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

相关文章

【力扣】Go语言回溯算法详细实现与方法论提炼

文章目录 一、引言二、回溯算法的核心概念三、组合问题1. LeetCode 77. 组合2. LeetCode 216. 组合总和III3. LeetCode 17. 电话号码的字母组合4. LeetCode 39. 组合总和5. LeetCode 40. 组合总和 II小结 四、分割问题6. LeetCode 131. 分割回文串7. LeetCode 93. 复原IP地址小…

HarmonyOS 私仓搭建

1. HarmonyOS 私仓搭建 私仓搭建文档&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-ohpm-repo-quickstart-V5   发布共享包[https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-har-publish-0000001597973129-V5]…

LabVIEW 离心泵机组故障诊断系统

开发了一套基于LabVIEW图形化编程语言设计的离心泵机组故障诊断系统。系统利用先进的数据采集技术和故障诊断方法&#xff0c;通过远程在线监测与分析&#xff0c;有效提升了离心泵的预测性维护能力&#xff0c;保证了石油化工生产的连续性和安全性。 项目背景及意义 离心泵作…

小林渗透入门:burpsuite+proxifier抓取小程序流量

目录 前提&#xff1a; 代理&#xff1a; proxifier&#xff1a; 步骤&#xff1a; bp证书安装 bp设置代理端口&#xff1a; proxifier设置规则&#xff1a; proxifier应用规则&#xff1a; 结果&#xff1a; 前提&#xff1a; 在介绍这两个工具具体实现方法之前&#xff0…

C++_STL_xx_番外01_关于STL的总结(常见容器的总结;关联式容器分类及特点;二叉树、二叉搜索树、AVL树(平衡二叉搜索树)、B树、红黑树)

文章目录 1. 常用容器总结2. 关联式容器分类3. 二叉树、二叉搜索树、AVL树、B树、红黑树 1. 常用容器总结 针对常用容器的一些总结&#xff1a; 2. 关联式容器分类 关联式容器分为两大类&#xff1a; 基于红黑树的set和map&#xff1b;基于hash表的unorder_set和unorder_ma…

Apache InLong数据集成工具安装部署和功能介绍

环境部署 在开始之前&#xff0c;我们需要安装 InLong 的全部组件 安装 ClickHouse 使用 Docker 快速部署 ClickHouse 数据库&#xff0c;命令如下&#xff1a; docker run -d --rm --nethost --name clickhouse -e CLICKHOUSE_USERadmin -e CLICKHOUSE_PASSWORDinlong -e C…

【开源免费】基于SpringBoot+Vue.JS网上超市系统(JAVA毕业设计)

本文项目编号 T 037 &#xff0c;文末自助获取源码 \color{red}{T037&#xff0c;文末自助获取源码} T037&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

练习LabVIEW第三十二题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第三十二题&#xff1a; 利用labview elapsed time(已用时间)定时设计输出一个方波 开始编写&#xff1a; 前面板放置一…

从“点”到“面”,热成像防爆手机如何为安全织就“透视网”?

市场上测温产品让人眼花缭乱&#xff0c;通过调研分析&#xff0c;小编发现测温枪占很高比重。但是&#xff0c;测温枪局限于显示单一数值信息&#xff0c;无法直观地展示物体的整体温度分布情况&#xff0c;而且几乎没有功能拓展能力。以AORO A23为代表的热成像防爆手机改变了…

恋爱脑学Rust之智能指针Rc,RefCell和Weak指针

小明和小丽为了维系彼此的关系&#xff0c;一起探索了智能指针的奥秘。通过 Rc、RefCell 和 Weak 的帮助&#xff0c;他们得以克服情感中遇到的种种困境。 第一章&#xff1a;Rc 智能指针的共生 小明和小丽搬进了一个共同的小屋&#xff0c;他们彼此相爱&#xff0c;决定共用…

C语言 | Leetcode C语言题解之第530题二叉搜索树的最小绝对差

题目&#xff1a; 题解&#xff1a; void dfs(struct TreeNode* root, int* pre, int* ans) {if (root NULL) {return;}dfs(root->left, pre, ans);if (*pre -1) {*pre root->val;} else {*ans fmin(*ans, root->val - (*pre));*pre root->val;}dfs(root->…

image_points_to_world_plane详解

分三个部分来聊聊这个算子 一,算子的参数介绍 二,算法的计算过程 三,举例实现 第一部分,算子的介绍 image_points_to_world_plane( : : CameraParam, WorldPose, Rows, Cols, Scale : X, Y) 参数介绍: CameraParam,:相机内参 WorldPose 世界坐标系,也叫物体坐标系(成…

“发放父作业单”是“过数”用例里面的内容吗

刘京城 2020-4-14 23:01 。。。。(注&#xff1a;这是一个人的昵称&#xff0c;不是省略号) 首先&#xff0c;执行者是同一个&#xff0c;那么思考焦点要关注“过数”用例是不是“发放父作业单”用例的一个步骤&#xff0c;和行为操作的频率无关&#xff0c;而是和责任有关&am…

vue3 ref,shallowRef,reactive,shallowReactive使用的简单异同点说明

1、这几个都是负责data的存储及读取的&#xff0c;我们常用的是ref,reactive。 2、看一下shallowRef这个,shallowReactive与其类似。 说明&#xff1a;以官网的说明&#xff0c;这个state.value.count 2是不会触发视图更新的&#xff0c;但是如果直接在<script setup lang…

Java | Leetcode Java题解之第517题超级洗衣机

题目&#xff1a; 题解&#xff1a; class Solution {public int findMinMoves(int[] machines) {int tot Arrays.stream(machines).sum();int n machines.length;if (tot % n ! 0) {return -1;}int avg tot / n;int ans 0, sum 0;for (int num : machines) {num - avg;s…

二分,CF 2036 G - Library of Magic

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 G - Library of Magic 二、解题报告 1、思路分析 首先 query(1, n) a ^…

Llama 3.2 Vision Molmo:多模态开源生态系统基础

编者按&#xff1a; 视觉功能的融入对模型能力和推理方式的影响如何&#xff1f;当我们需要一个既能看懂图像、又能生成文本的 AI 助手时&#xff0c;是否只能依赖于 GPT-4V 这样的闭源解决方案&#xff1f; 我们今天为大家分享的这篇文章&#xff0c;作者的核心观点是&#xf…

【android12】【AHandler】【4.AHandler原理篇ALooper类方法全解】

AHandler系列 【android12】【AHandler】【1.AHandler异步无回复消息原理篇】-CSDN博客 【android12】【AHandler】【2.AHandler异步回复消息原理篇】-CSDN博客 【android12】【AHandler】【3.AHandler原理篇AHandler类方法全解】-CSDN博客 其他系列 本人系列文章-CSDN博客…

探索NetCat:网络流量监测与数据传输的利器

从简单的数据传输到复杂的网络调试&#xff0c;NetCat的灵活性和多功能性让人赞叹不已&#xff0c;在这篇文章中我将深入探讨NetCat的魅力&#xff0c;揭示它的基本功能、实用技巧以及在日常工作中的应用场景&#xff0c;发现如何用这一小工具提升的网络技能与效率。 目录 Net…

UE4安卓Gradle工程中的libUE4.so的生成原理

流程图 流程图放在最前面&#xff0c;下面是讲解。 libUE4.so 问&#xff1a;在UE4安卓开发中&#xff0c;libUE4.so即是符号表&#xff0c;又是引擎代码native&#xff0c;是吗&#xff1f; 答&#xff1a;是的&#xff0c;libUE4.so在UE4安卓开发中既包含符号表&#xff0c;…