C++泛型编程:模版

引言        

泛型编程(Generic Programming)是一种编程范式,允许编写与类型无关的代码,从而使程序更加灵活和可重用。在C++中,泛型编程主要通过模板(Templates)来实现。模板使得我们可以编写通用函数和类,从而在不同类型之间复用相同的算法或逻辑。这样,程序的灵活性和可扩展性得到了极大的提升。

例如一个交换函数,我们可能由于不同的参数类型,要重载多个函数。

void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}

而c++的模版便是告诉编译器一个模子,让编译器根据不同的类型通过该模子来生成代码,只需要下面一段代码就能实现上面的所有功能

template<typename T>
T Add(const T& x, const T& y)
{T tmp = x;x = y;y = tmp;
}

接下来,让我们一起来深入了解 C++ 模板的概念、用法以及一些高级特性吧。 

1.函数模版

函数模板允许我们创建一个可以处理不同类型参数的函数。

1.1基本语法

template <typename T>
T functionName(T arg1, T arg2) {// 函数体
}

 template <typename T>:定义一个模版,T是类型参数(类型参数可自定义)

T functionName (T arg1, T arg2):函数返回类型和参数类型均为模板参数

1.2实例

隐式化实例:

#include <iostream>
using namespace std;
template<typename T>
T Add(const T& x, const T& y)
{return x + y;
}int main()
{int a1 = 10;int a2 = 20;double b1 = 10.1;double b2 = 20.1;Add(a1, a2);Add(b1, b2);//没有与参数列表匹配的函数模版实例//Add(a1, b1);// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化Add(a1, (int)b1);return 0;
}

显示化实例:在函数名后的<>中指定模版参数的实际类型

int main()
{int a1 = 10;double b1 = 10.1;Add<int>(a1, b1);return 0;
}

1.3模版参数匹配原则 

完全匹配优先,非模板函数优先

当调用模板时,编译器首先尝试找到与调用模版参数类型完全匹配的模版实例,如果非模板函数参数完全匹配,则优先选择非模板函数

// 专门处理int的加法函数
int Add(int left, int right)
{return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}
void Test()
{Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}

1.4函数模板默认参数

函数模板的默认参数是在函数模板定义中为模板参数指定的默认值。当使用函数模板时,如果没有为相应的模板参数提供具体的值,就会使用默认参数。
以下是一个函数模板默认参数的示例:

#include <iostream>
using namespace std;template<typename T = int, int N = 10>
void printArray(T arr[]) {for (int i = 0; i < N; i++) {cout << arr[i] << " ";}cout << std::endl;
}int main() {int intArray[] = {1, 2, 3, 4, 5};printArray(intArray); // 使用默认参数,T 为 int,N 为 10double doubleArray[] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7};printArray<double, 7>(doubleArray); // 显式指定模板参数,T 为 double,N 为 7return 0;
}


在这个例子中,函数模板 printArray 有两个模板参数 T 和 N ,分别表示数组元素的类型和数组的大小。 T 的默认类型是 int , N 的默认值是 10 。
 
需要注意的是,函数模板默认参数的指定应该遵循一定的规则:
 
1. 默认参数只能从右向左依次提供,即如果某个模板参数有默认参数,那么它右边的所有模板参数也必须有默认参数。
2. 在函数模板调用时,如果要为某个模板参数提供具体的值,那么它左边的所有模板参数也必须显式地指定。

2.类模版

类模板允许我们创建可以处理任意数据类型的类

2.1类模板的基本语法

类模板的语法类似于函数模板。我们通过template关键字引入类型参数(或其他参数),以定义一个类模板。常见的形式是:

template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};

这里的T是一个模板参数,表示将来可以用来替代任何类型。Class类中的成员变量、构造函数和成员函数都使用模板参数T,因此该类可以处理不同类型的数据。

2.2实例

// 类模版
template<typename T>
class Stack
{
public:Stack(size_t capacity = 4){_array = new T[capacity];_capacity = capacity;_size = 0;}
void Push(const T& data);
private:T* _array;size_t _capacity;size_t _size;
};

2.3非类型模板参数

除了类型参数外,类模板还可以接受非类型的模板参数。非类型参数通常用于定义与类型无关的值,例如数组的大小、常量等。例子如下:

template<typename T, int Size>
class Array {
private:T arr[Size];
public:Array() {for (int i = 0; i < Size; ++i) {arr[i] = 0;}}T& operator[](int index) {return arr[index];}int getSize() const {return Size;}
};

在这里,`Array`类模板接受一个类型参数`T`和一个整数参数`Size`,用于定义数组的大小。该模板可以实例化不同类型和大小的数组。

使用方式:

int main() {Array<int, 5> intArray;  // 定义一个包含 5 个 int 元素的数组intArray[0] = 10;std::cout << intArray[0] << std::endl;  // 输出 10std::cout << "Size: " << intArray.getSize() << std::endl;  // 输出 5
}

2.4. 类模板的默认参数

类模板和函数模板一样,也可以为模板参数提供默认值。

template<typename T = int>
class MyClass {
private:T value;
public:MyClass(T v) : value(v) {}T getValue() const { return value; }
};int main() {MyClass<> obj(10); // 使用默认的 int 类型std::cout << obj.getValue() << std::endl; // 输出 10
}

在这里,如果没有提供模板参数,MyClass会默认使用int类型

需要注意的是,类模板默认参数的指定也要遵循一定的规则:
 
1. 默认参数只能从右向左依次提供,即如果某个模板参数有默认参数,那么它右边的所有模板参数也必须有默认参数。
2. 在函数模板调用时,如果要为某个模板参数提供具体的值,那么它左边的所有模板参数也必须显式地指定。

3.模板的特化

模板特化允许我们为特定类型定义不同于通用模板的特殊实现。当通用的模板定义不能满足某些特定类型的需求时,可以通过模板特化来提供专门的实现。类模板的特化分为完全特化部分特化

- 例如,假设有一个通用的模板函数用于比较两个值的大小:

template<typename T>
bool compare(T a, T b) {return a < b;
}

这个函数可以比较任何类型的值。但是,如果对于特定类型,如指针类型,需要不同的比较方式,可以进行模板特化:

template<>
bool compare<int*>(int* a, int* b) {return *a < *b;
}


在这个特化版本中,专门针对 int* 类型的指针进行了比较,比较的是指针所指向的值的大小,而不是指针本身的地址大小。 

3.1模板的完全特化

函数模板的完全特化:

函数模板的特化可以为某种具体类型提供定制的实现:

#include <iostream>template<typename T>
void func(T value) {std::cout << "General template: " << value << std::endl;
}// 完全特化,针对 char* 类型
template<>
void func<char*>(char* value) {std::cout << "Specialized for char*: " << value << std::endl;
}int main() {func(10);           // 调用通用模板,输出 "General template: 10"func("Hello");      // 调用特化版本,输出 "Specialized for char*: Hello"
}


在这个例子中,函数模板被特化为char*类型,并为该类型提供了不同的实现。

类模板的完全特化:

类模板的完全特化是针对某一特定类型提供特殊的实现。例如,我们为bool类型特化一个类模板:

template<typename T>
class MyClass {
public:void print() {std::cout << "General template\n";}
};// 针对 bool 类型进行完全特化
template<>
class MyClass<bool> {
public:void print() {std::cout << "Specialized for bool\n";}
};int main() {MyClass<int> obj1;obj1.print();  // 输出 General templateMyClass<bool> obj2;obj2.print();  // 输出 Specialized for bool
}

在这个例子中,当模板参数为bool时,调用特化版本,而其他类型调用通用模板。

3.2模板的部分特化

部分特化是指特化模板的某些参数,而不是全部参数。类模板可以进行部分特化,但函数模板不能进行部分特化

(1)类模板的部分特化

#include <iostream>// 通用模板
template<typename T, typename U>
class MyClass {
public:void display() {std::cout << "General template\n";}
};// 部分特化,当第二个参数是 int 时
template<typename T>
class MyClass<T, int> {
public:void display() {std::cout << "Partially specialized template for second parameter int\n";}
};int main() {MyClass<double, double> obj1;obj1.display();  // 输出 "General template"MyClass<double, int> obj2;obj2.display();  // 输出 "Partially specialized template for second parameter int"
}

在这个例子中,当第二个模板参数为int时,会使用特化的模板。其他类型组合仍然使用通用模板。

(2)指针和引用的部分特化

部分特化还可以用于特定的类型模式,例如指针或引用类型。如下例:

#include <iostream>// 通用模板
template<typename T>
class MyClass {
public:void display() {std::cout << "General template\n";}
};// 部分特化,针对指针类型
template<typename T>
class MyClass<T*> {
public:void display() {std::cout << "Specialized template for pointer types\n";}
};int main() {MyClass<int> obj1;obj1.display();  // 输出 "General template"MyClass<int*> obj2;obj2.display();  // 输出 "Specialized template for pointer types"
}

在这个例子中,MyClass<int*>会匹配到特化的指针类型版本,而MyClass<int>仍然使用通用模板。

3.3. 模板特化与继承

- 在 C++中,可以结合模板和继承来创建更加灵活和可扩展的类层次结构。通过模板参数,可以在基类中定义一些通用的功能,而派生类可以根据具体的需求进行特化或扩展。
- 例如,考虑一个通用的容器类模板:

template<typename T>
class Container {
public:void add(T item) {// 添加元素到容器的通用实现}// 其他通用的容器操作
};

然后,可以创建一个派生类来特化这个容器类,例如一个只存储整数的容器:

class IntContainer : public Container<int> {
public:// 可以添加针对整数容器的特殊操作
};

在这个例子中, IntContainer 继承自 Container<int> ,继承了通用容器类的功能,并可以根据整数的特点添加特定的操作。

#include <iostream>
using namespace std;
template<typename T>
class Base {
public:void print() {cout << "Base template\n";}
};// 特化 Base 类,针对 int 类型
template<>
class Base<int> {
public:void print() {cout << "Specialized Base template for int\n";}
};template<typename T>
class Derived : public Base<T> {
public:void show() {cout << "Derived template\n";this->print();}
};int main() {Derived<double> obj1;obj1.show();  // 调用通用模板,输出 "Base template"Derived<int> obj2;obj2.show();  // 调用特化模板,输出 "Specialized Base template for int"
}

在这个例子中,基类Base<int>进行了完全特化,当派生类继承自Base<int>时,它将使用特化版本的print()函数,而其他类型使用通用版本。

4. 模板特化的应用场景

模板特化通常用于以下场景:

- 处理某些类型的特殊需求:例如,对bool类型、指针类型、数组类型等进行特殊处理。
- 针对容器或算法进行优化:在某些类型上进行优化以提高性能,例如对于std::vector<bool>的特殊优化。
- 处理特定类型的不同行为:例如,针对浮点数和整数提供不同的处理逻辑。
  

模板特化是C++泛型编程中非常强大且灵活的特性。通过模板特化,程序员可以为某些类型提供特定的处理方式,而不影响其他类型的通用逻辑。理解和合理使用模板特化可以让代码更加高效、灵活。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++学习指南(六)----list

欢迎来到繁星的CSDN。本期内容主要包括&#xff0c;list的介绍、使用以及与vector的优缺点。 一、什么是list 在先前的C语言学习中&#xff0c;我们接触到了顺序表和链表&#xff0c;而在C中&#xff0c;这正好对应了vector&#xff08;动态增长顺序表&#xff09;和l…

linux第三课(linux中安装nginx与redis及SpringBoot集成redis)

目录 一.nginx引入 二.关于nginx 1.什么是nginx 2.nginx的特点 3.在nginx中安装nginx 三.关于redis 1.背景引入 2.什么是redis 3.redis的特点 4.在linux下的docker中安装redis 四.redis中的数据结构 (1)String(字符串) (2)Hash (3)list(列表) (5)zset(sorted se…

Python模拟鼠标轨迹[Python]

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.鼠标轨迹算法实…

博睿谷IT认证-订阅试学习

在这个信息爆炸的时代&#xff0c;拥有一张IT认证证书&#xff0c;就像拿到了职场晋升的通行证。博睿谷&#xff0c;作为IT认证培训的佼佼者&#xff0c;帮你轻松拿下华为、Oracle等热门认证。下面&#xff0c;让我们一起看看博睿谷如何助你一臂之力。 学习时间&#xff0c;你说…

C++入门基础知识82(实例)——实例7【 判断一个数是奇数还是偶数】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 实例 【判断一个数是奇数还是偶数】相…

java重点学习-总结

十五 总结 https://kdocs.cn/l/crbMWc8xEZda &#xff08;总结全部的精华&#xff09; 1.面试准备 企业筛选简历规则简历编写注意事项(亮点)项目怎么找&#xff0c;学习到什么程度面试过程(表达结构、什么样的心态去找工作) 2.redis 缓存相关(缓存击穿、穿透、雪崩、缓存过期淘…

传输层协议 —— TCP协议(上篇)

目录 1.认识TCP 2.TCP协议段格式 3.可靠性保证的机制 确认应答机制 超时重传机制 连接管理机制 三次握手 四次挥手 1.认识TCP 在网络通信模型中&#xff0c;传输层有两个经典的协议&#xff0c;分别是UDP协议和TCP协议。其中TCP协议全称为传输控制协议&#xff08;Tra…

远程升级频频失败?你可能忽略了模组差分包…

去年开发的一个项目产品&#xff0c;用的是合宙4G-Cat.1低功耗模块Air780E。 最近有客户反馈在乡村里频繁出现掉线的情况。通过换货、换SIM卡对比排查测试&#xff0c;发现只有去年5月22号采购的那批模块在客户环境附近会出现掉线的情况&#xff0c;而今年4月份采购的模块批次…

【Go】Go 环境下载与安装教程(Windows系统)

引言 Go&#xff0c;也被称为Golang&#xff0c;是一种静态类型&#xff0c;编译型的编程语言&#xff0c;由Google设计和开发。Go语言的设计目标是“解决软件开发中的一些问题”&#xff0c;特别是在大规模软件系统的构建和维护方面。 下载安装包 打开官网下载页面&#xff…

03 添加并发请求

03 添加并发请求 我们通过两种方式演示发起多个请求&#xff1a; 使用 async 和 await 方式使用 Promise.all() 方式 首先使用async 和 await 方式发送请求&#xff0c;使用 async 和 await 能够控制异步任务以同步的流程执行&#xff0c;代码如下&#xff0c;这时候就会产生…

Git 提交规范

一、Git 提交规范的基本格式 通常&#xff0c;Git 提交信息采用以下格式&#xff1a; <type>: <subject><body><footer>type&#xff08;提交类型&#xff09;&#xff1a;用于说明提交的性质&#xff0c;常见的类型有以下几种&#xff1a; feat&…

仓颉编程语言4,遇到BUG求助

本来准备整仓颉链接Mysql数据库。参考&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 这种方式是拿mysql官方的dll&#xff0c;编译一下&#xff0c;然后再封装成仓颉数据库驱动。这种方式不够逼格&#xff0c;所以准备解析mysql网络协议&#xff0c;从0开始写…

cmd快速进入文件夹目录下

首先&#xff0c;将文件夹直接点击左键拖动至cmd窗口中&#xff0c;就可以得到目录路径。 还有就是&#xff0c;在命令行直接敲入D:或者C:就可以在磁盘之间进行转换&#xff0c;注意冒号不要丢。 再有&#xff0c;如果进入某磁盘中的一个文件夹&#xff0c;使用cd命令。路径获取…

SpringBoot实战(三十)发送HTTP/HTTPS请求的五种实现方式【下篇】(Okhttp3、RestTemplate、Hutool)

目录 一、五种实现方式对比结果二、Demo接口地址实现方式三、Okhttp3 库实现3.1 简介3.2 Maven依赖3.3 配置文件3.4 配置类3.5 工具类3.6 示例代码3.7 执行结果实现方式四、Spring 的 RestTemplate 实现4.1 简介4.2 Maven依赖4.3 配置文件4.4 配置类4.5 HttpClient 和 RestTemp…

Parallels Desktop 20 for Mac 推出:完美兼容 macOS Sequoia 与 Win11 24H2

Parallels Desktop 20 for Mac 近日正式发布&#xff0c;这一新版本不仅全面支持 macOS Sequoia 和 Windows 11 24H2&#xff0c;还在企业版中引入了一个全新的管理门户。新版本针对 Windows、macOS 和 Linux 虚拟机进行了多项改进&#xff0c;其中最引人注目的当属 Parallels …

导出导入Oracle数据库使用黑框命令方式exp、imp【亲测】

下载工具 根据自己数据库的版本下载&#xff0c;以v19为例&#xff1a; 下载基础包Basic Package和工具包Tools Package 两个压缩包中的文件夹一样&#xff0c;但内容不一样&#xff0c;将两个压缩包中的文件解压合并到一起 https://www.oracle.com/database/technologies/inst…

SpringCloud入门(六)Nacos注册中心(下)

一、Nacos环境隔离 Nacos提供了namespace来实现环境隔离功能。 nacos中可以有多个namespace。namespace下可以有group、service等。不同namespace之间相互隔离&#xff0c;例如不同namespace的服务互相不可见。 使用Nacos Namespace 环境隔离 步骤&#xff1a; 1.在Nacos控制…

时间序列无监督异常点检测算法_孤立森林,局部离群因子检测和自编码器

数据入口&#xff1a;压气机异常检测一维时间序列 - Heywhale.com 该数据为采样自工业压气机的一维时间序列数据。本文将通过无监督时间序列算法进行时间序列异常检测。针对时间序列数据&#xff0c;常用的无监督异常检测算法包括&#xff1a;孤立森林&#xff08;Isolation Fo…

【Yonghong星球】Windows平台上Yonghong的Python、DM-Engine安装与配置详细攻略

文章目录 问题描述问题解决(配置相应的python计算服务)拓展 第三方工具包安装/更新其他出现问题 问题描述 当我们进行深度分析的时候&#xff0c;运行结点报错&#xff0c;这是因为需要配置相应的python计算服务。 报错内容&#xff1a; 2024-09-20 13:57:22 开始运行“各省G…

js中的 赋值 浅拷贝 和 深拷贝 详细解读

js数据类型主要分基本数据类型和引用数据类型。前者包括Number,String等&#xff0c;后者主要是Object,因此以下会针对不同的数据类型来分析,需要的朋友可以参考一下 基本数据类型&#xff08;Primary Data Types&#xff09;: String&#xff08;字符串&#xff09; Number&…