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++泛型编程中非常强大且灵活的特性。通过模板特化,程序员可以为某些类型提供特定的处理方式,而不影响其他类型的通用逻辑。理解和合理使用模板特化可以让代码更加高效、灵活。
相关文章:
C++泛型编程:模版
引言 泛型编程(Generic Programming)是一种编程范式,允许编写与类型无关的代码,从而使程序更加灵活和可重用。在C中,泛型编程主要通过模板(Templates)来实现。模板使得我们可以编写通用…...
一道涉及 Go 中的并发安全和数据竞态(Race Condition)控制的难题
这是一道涉及 Go 中的并发安全和数据竞态(Race Condition)控制的难题。 问题描述: 你需要实现一个并发安全的计数器 SafeCounter,该计数器允许多个 Goroutine 同时对其进行读写操作。计数器会存储每个键的计数值。 具体要求&am…...
如何降低H5商城系统的开发成本
前言 H5商城系统通过多种策略来降低开发成本。以下是对这些策略的详细介绍: 一、选择合适的开发平台 原生开发与跨平台开发:原生开发使用HTML5、CSS3和JavaScript等Web技术,虽然性能更佳、用户体验更好,但开发成本相对较高。而…...

为什么越来越多的网工运维转行网络安全?_idc运维转网络安全工程师_系统运维转行网安
最近越来越多的网工运维小伙伴都在吐槽:干网工、运维多年,薪资还是5.6K,技术也遇瓶颈上不去,考虑转岗或者转行。其中大部分的网工运维小伙伴们纷纷瞄准了高薪高前景的网络安全工程师岗位 网络安全是怎样的岗位? 网络安…...

【TabBar嵌套Navigation案例-产品推荐页面-UICollectionView-结合xib使用 Objective-C语言】
一、接下来,我们来说这个产品推荐页面 1.首先呢,它是一个CollectionViewController,当我点击这个产品推荐的时候, 这个Cell的时候,我要跳到一个CollectionViewController, 所以呢,我们需要先找到产品推荐,然后给它去添加一个targetVC,然后给它push到一个产品推荐的页面…...
java.nio.ByteBuffer的 capacity, limit, position, mark
java.nio.ByteBuffer的 capacity, limit, position, mark Capacity(容量) 定义:缓冲区的总容量,即缓冲区中可以容纳的元素的数量。这个容量在缓冲区创建时被设定,并且之后不能被改变。 用途:它定义了缓冲区…...

握手传输 状态机序列检测(记忆科技笔试题)_2024年9月2日
发送模块循环发送0-7,在每个数据传输完成后,间隔5个clk,发送下一个 插入寄存器打拍处理,可以在不同的时钟周期内对信号进行同步,从而减少亚稳态的风险。 记忆科技笔试题:检测出11011在下一个时钟周期输出…...

电商跨境电商商城系统/网上商城接口/电商数据接口详情
电商API接口背景:电商运营中,数据分析这项工作越来越重要,许多品牌方也越来越热衷去做电商数据分析。不过,全面的数据该如何获取呢,此时,电商数据接口的重要性便凸显出来了。 电商API数据接口主要有以下特…...

openFrameworks_如何使用ofxXmlSettings和ofxGui来创建识别界面
效果图: 代码及详解 1.添加两个插件的头文件: #include "ofxGui.h" #include "ofxXmlSettings/src/ofxXmlSettings.h" 2.添加GUI部分,然后在.h声明右边的openframeworks的UI部分,包括面板ofxPanel,按钮ofx…...

180多个GIS地理空间定义术语中英文对照配图
主动传感器(Active Sensors): [遥感]主动传感器照亮其目标,并测量返回到传感器的反射后向散射。 邻接(Adjacency): [几何]邻接发生在两个对象共享同一边界,并且与公共边或顶点相邻…...

Vue(14)——组合式API①
setup 特点:执行实际比beforeCreate还要早,并且获取不到this <script> export default{setup(){console.log(setup函数);},beforeCreate(){console.log(beforeCreate函数);} } </script> 在setup函数中提供的数据和方法,想要在…...

【图像检索】基于颜色模型的图像内容检索,matlab实现
博主简介:matlab图像代码项目合作(扣扣:3249726188) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本次案例是基于颜色模型的图像内容检索,用matlab实现。 一、案例背景和算法介绍 这…...
看过来——量子计算中一个神奇符号的解释
量子计算中一个神奇符号是 H ⊗ n \mathcal{H}^{\otimes n} H⊗n 它代表什么呢, 往下看 H ⊗ n \mathcal{H}^{\otimes n} H⊗n 通常在量子力学中表示 n次张量积的希尔伯特空间。 H \mathcal{H} H 表示一个希尔伯特空间,这是量子力学中描述量子态的空间&…...

传输层 IV(TCP协议——流量控制、拥塞控制)【★★★★】
(★★)代表非常重要的知识点,(★)代表重要的知识点。 一、TCP 流量控制(★★) 1. 利用滑动窗口实现流量控制 一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得…...

Java设计模式全面解析
23大设计模式(即软件设计中的24种常用设计模式)源自《设计模式:可复用面向对象软件的基础》一书,由四位作者(Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)提出,通常也被称为“Go…...
spring全家桶使用教程
Spring 全家桶是指围绕 Spring 框架构建的一系列子项目和工具,涵盖了企业级应用开发的多个方面,如依赖注入、数据访问、事务管理、Web 开发、消息队列、云服务等。通过 Spring 全家桶,开发者可以构建从简单的 Web 应用到复杂的微服务架构。 …...
REST-系统架构师(六十九)
1某公司内部的信息系统集成,需要实现在系统之间快速传递可定制格式的数据包,并且当有新的数据包到达时候,接收系统会自动得到通知。另外还要支持数据重传,以确保传输的成功。针对这些需求,应该采用(&#x…...

SAP B1 营销单据 - 复制从复制到总结
背景 营销单据具有相似的表单结构,并且单据之间可互相复制,本文总结出各个单据可【复制从】与【复制到】的单据清单,并绘制流程图,表现理论上可完成的流程。 销售:销售报价单;销售订单;交货&am…...

css设置overflow:hiden行内元素会发生偏移的现象
父级元素包含几个行内元素 <div id"box"><p><span>按钮</span><span>测试文字文字文字测试文字文字文字</span><span>看这里</span></p></div>#box p{width: 800px;font-size: 30px;}#box p span{disp…...
使用多个 GitHub 账号的 SSH 配置与常见问题排查
文章目录 使用多个 GitHub 账号的 SSH 配置与常见问题排查摘要目录1. 使用多个 GitHub 账号的场景介绍2. 配置多个 SSH 密钥2.1 生成多个 SSH 密钥2.2 添加 SSH 密钥到 SSH 代理2.3 将 SSH 公钥添加到 GitHub 账户 3. 配置 SSH 代理与 GitHub 账户的关联3.1 为不同仓库设置不同…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...