模板的进阶

news2024/12/24 21:22:48

目录

1.非类型模板参数

2.模板特化

2.1概念

2.2函数模板特化

2.3类模板特化

2.3.1全特化

2.3.2偏特化

3.模板分离编译

3.1什么是分离编译

3.2 模板的分离编译

3.3解决方法

4. 模板总结


1.非类型模板参数

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

// 定义一个模板类型的静态数组
//非类型模板参数
//1.它是常量不可进行修改
//2.必须是整形
template<class T, size_t N = 10>//N是非类型模板参数
class Stack
{
private:
	T _a[N];
	int _top;
};

int main()
{
	Stack<int, 10> st1;
	Stack<int, 100> st2;
}

注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果,所以可以加上缺省值。

2.模板特化

2.1概念

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

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

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

	int a = 30, b = 20;
	cout << Less(a, b) << endl; //结果正确

	cout << Less(&a, &b) << endl; //结果错误

	return 0;
}

可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。
此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化类模板特化

2.2函数模板特化

函数模板的特化步骤:

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

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

//第一种方法
//函数模板的特化
template<>
bool Less<int*>(int* left, int* right)
{
	return *left < *right;
}

//第二种方法
template<class T>
bool Less(T* left, T* right)
{
	return *left < *right;
}

//函数重载
bool Less(int* left, int* right)
{
	return *left < *right;
}

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

	int a = 30, b = 20;
	cout << Less(a, b) << endl; //结果正确
	cout << Less(&a, &b) << endl; //结果错误

	double c = 1.1, d = 2.2;
	cout << Less(&c, &d) << endl;

	return 0;
}

2.3类模板特化

类模板的特化步骤:

1. 必须要先有一个基础的普通类模板
2. 关键字template后面接一对空的尖括号<>
3. 类名后跟一对尖括号,尖括号中指定需要特化的类型
4. 模板类参数表里面的类型个数要和普通类模板的类型个数一样

2.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;
};
int main()
{
	Data<int, int> d1;
	Data<int, char> d2;

	return 0;
}

2.3.2偏特化

偏特化有以下两种表现方式:
1.部分特化,将模板参数类表中的一部分参数特化

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

//普通类模板
template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
//1.将第二个参数特化为int
template <class T1>
class Data<T1, int>//部分特化,将模板类参数表中的一部分参数特化
{
public:
	Data() { cout << "Data<T1, int>" << endl; }
private:
	T1 _d1;
	int _d2;
};

//2.参数更进一步的限制偏特化并不仅仅是指特化部分参数,而是
//针对模板参数更进一步的条件限制所设计出来的一个特化版本。
//两个参数偏特化为指针类型
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); // 调用特化的指针版本
}

注:这些特化模板是新的类吗?肯定不是,因为他们不是独立存在的,他们是建立在普通类模板的上面,不需要把他们的所有成员变量和成员函数全部再写一遍,只需要在你特化出来的类里面实现需要该特化类里面才能实现的功能的成员函数或者成员变量即可也就是说根据需求来写,但是模板实例化出来的东西是独立存在的是新的东西。

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"test.h"
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);
	return 0;
}

分析:

3.3解决方法

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

//a.h
#pragma once
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

//main.cpp
#include"test.h"

int main()
{
	Add(1, 2);
	Add(1.0, 2.0);
	return 0;
}

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

3、由头文件"xxx.hpp"或者"xxx.h"使用include包含源文件"xxx.cpp"

//a.h
#pragma once

#include"test.cpp"

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"test.h"

int main()
{
	Add(1, 2);
	Add(1.0, 2.0);
	return 0;
}

4. 模板总结

优点:
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
缺陷:
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

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

相关文章

smtplib.SMTPHeloError: (500, b‘Error: bad syntax‘)

如果你编写邮件收发工具的时候,有可能会遇到这个问题。这里直接给出解决办法。 目录 1、检查系统版本 2、点击右侧的更改适配器选项

Nginx负载均衡(重点)

正向代理 部署正向代理 server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; proxy_pass http://20.0.0.60:80…

【雕爷学编程】Arduino动手做(05)---热敏电阻传感器模块之热电效应的原理以及相关应用

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

SD-MTSP:光谱优化算法LSO求解单仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、光谱优化算法LSO 光谱优化算法&#xff08;Light Spectrum Optimizer&#xff0c;LSO&#xff09;由Mohamed Abdel-Basset等人于2022年提出。 参考文献&#xff1a; [1]Abdel-Basset M, Mohamed R, Sallam KM, Chakrabortty RK. Light Spectrum Optimizer: A Novel Physi…

list的使用和模拟实现

目录 1.list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 2.为什么使用迭代器&#xff1f; 3.list的模拟实现 3.1完整代码 3.2代码解析 4.list与…

MySQL索引优化分析

MySQL索引优化分析 为什么你写的sql查询慢&#xff1f;为什么你建的索引常失效&#xff1f;通过本章内容&#xff0c;你将学会MySQL性能下降的原因&#xff0c;索引的简介&#xff0c;索引创建的原则&#xff0c;explain命令的使用&#xff0c;以及explain输出字段的意义。助你…

Servlet介绍

1.简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一&#xff0c;其实就是一个接口&#xff0c;将来我们需要定义Servlet类…

C++小游戏贪吃蛇源码

graphics.h是针对DOS下的一个C语言图形库 (c也可以) 目前支持下载此头文件的常用的有两种: 1. EGE (Easy Graphics Engine)2. EasyX Graphics LibraryEGE, 全名Easy Graphics Engine, 是windows下的简易绘图库&#xff0c;是一个类似BGI(graphics.h)的面向C/C语言新手的图形库…

win10+Vmware+ubuntu18 mosquitto调试记录

记录一下在建立mqtt调试环境上遇到的问题及对策。 我的PC环境为&#xff0c;win10为办公环境&#xff0c;Vmware虚拟机安装ubuntu18&#xff0c;虚拟机主要用来进行代码编译&#xff0c;建立mosquitto server测试环境。 1. ubuntu 安装mosquitto 安装mosquitto网上很多教程&…

CDN(内容分发网络)

CDN的全称是 Content Delivery Network, 即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c;降低网络拥塞&a…

音乐播放器应用——Android开发组件化实例分析

组件化技术在Android开发中扮演着重要的角色&#xff0c;它能够将一个庞大的应用拆分成多个独立的组件&#xff0c;每个组件都可以独立开发、测试和维护。本文将深入探讨组件化技术的认知、优势以及搭建过程&#xff0c;并通过代码示例解析组件化实例分析。最后&#xff0c;我们…

C++:类与对象(下)- this指针、(拷贝)构造函数、析构函数、复制运算符重载

目录 一、 this指针 1.1 引入 1.2 问题 1.3 特性 二、 构造函数 2.1 概念 2.2 特性 2.3 语法 2.4 注意点 三、 析构函数 3.1 概念 3.2 特性 3.3 示例 四、拷贝构造函数 4.1 概念 4.2 特性 4.3 示例 4.4 深浅拷贝 五、 赋值运算符重载 5.1 概念 5.2 语法 5…

10-数据结构-队列(C语言)

队列 目录 目录 队列 一、队列基础知识 二、队列的基本操作 1.顺序存储 ​编辑 &#xff08;1&#xff09;顺序存储 &#xff08;2&#xff09;初始化及队空队满 &#xff08;3&#xff09;入队 &#xff08;4&#xff09;出队 &#xff08;5&#xff09;打印队列 &…

编写一个指令(v-focus2end)使输入框文本在聚焦时焦点在文本最后一个位置

项目反馈输入框内容比较多时候&#xff0c;让鼠标光标在最后一个位置&#xff0c;心想什么奇葩需求&#xff0c;后面试了一下&#xff0c;是有点影响体验&#xff0c;于是就有了下面的效果&#xff0c;我目前的项目都是若依的架子&#xff0c;用的是vue2版本。vue3的朋友想要使…

什么是POP3协议?

POP3&#xff08;Post Office Protocol Version 3&#xff09;是一个用于从电子邮件服务器获取邮件的应用层协议。以下是关于POP3的详细解释&#xff1a; 基本操作&#xff1a;使用POP3&#xff0c;电子邮件客户端可以从邮件服务器上下载电子邮件&#xff0c;并将其保存在本地。…

Unity制作护盾——3、蜂窝晶体护盾

Unity制作晶格护盾 大家好&#xff0c;我是阿赵。 继续来做护盾&#xff0c;这一期做一个蜂窝晶体护盾的效果。 一、效果展示 这个晶体护盾的特点是&#xff0c;整个护盾是由很多五边形和六边形的晶体构成&#xff0c;每一块晶体的颜色都在不停的变化&#xff0c;然后每一块晶…

使用 Spring Boot 发送电子邮件(SMTP 集成)

本文探讨了 Spring Boot 与 SMTP 的集成以及如何从您自己的 Spring Boot 应用程序发送电子邮件。 本文探讨如何从您自己的Spring Boot应用程序发送电子邮件。 是的&#xff0c;您可以拥有专用的 REST API&#xff0c;它接受电子邮件发送者和接收者的电子邮件地址、主题…

【雕爷学编程】Arduino动手做(05)---热敏电阻模块之的基本参数、模块特色、电原理与使用说明

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…

SpringBoot 整合Druid

集成Druid Druid简介 Java程序很大一部分要操作数据库&#xff0c;为了提高性能操作数据库的时候&#xff0c;又不得不使用数据库连接池。 Druid 是阿里巴巴开源平台上一个数据库连接池实现&#xff0c;结合了 C3P0、DBCP 等 DB 池的优点&#xff0c;同时加入了日志监控。 D…

Chrome DevTools 与 WebSocket 数据查看失焦的问题

Chrome DevTools 在与 WebSocket 连接交互时可能会出现失焦的问题&#xff0c;这似乎是一个已知的 bug。当 DevTools 选中 WebSocket 消息时&#xff0c;如果有新的消息到达&#xff0c;DevTools 将会自动失焦&#xff0c;导致无法查看完整的消息内容。 虽然这个问题很令人困扰…