C++基础知识【8】模板
目录
一、什么是C++模板?
二、函数模板
三、类模板
四、模板特化
五、模板参数
六、可变模板参数
七、模板元编程
八、嵌套模板
九、注意事项
一、什么是C++模板?
C++模板是C++编程中非常重要的一部分,它允许程序员以一种通用的方式编写代码,以便代码可以在不同类型之间进行重用。
那么,什么是C++模板?C++模板是一种允许程序员编写通用代码的机制。它们允许函数、类和数据类型适用于不同类型的参数,使得代码可以在不同类型之间进行重用。
二、函数模板
函数模板是一种允许程序员编写一个通用函数的机制。函数模板定义了一个函数,它可以适用于不同类型的参数。例如:
template <typename T>
T max(T a, T b) {return (a > b) ? a : b;
}
上面的代码定义了一个函数模板,它可以接受不同类型的参数。当函数被调用时,编译器会根据传递的参数类型推断出T的实际类型。
例如,以下代码将调用max函数,使用double类型的参数:
double result = max(3.14, 2.71);
可以使用函数模板来实现一个通用的排序算法,如快速排序
template<typename T>
void quicksort(T* array, int left, int right) {int i = left, j = right;T pivot = array[(left + right) / 2];while (i <= j) {while (array[i] < pivot) i++;while (array[j] > pivot) j--;if (i <= j) {std::swap(array[i], array[j]);i++;j--;}}if (left < j) quicksort(array, left, j);if (i < right) quicksort(array, i, right);
}
三、类模板
类模板是一种允许程序员编写通用类的机制。类模板定义了一个类,它可以适用于不同类型的参数。例如:
template <typename T>
class Stack {
private:vector<T> elements;
public:void push(T const&); void pop(); T top() const; bool empty() const { return elements.empty();}
};template <typename T>
void Stack<T>::push(T const& element) {elements.push_back(element);
}template <typename T>
void Stack<T>::pop() {if (elements.empty()) {throw out_of_range("Stack<>::pop(): empty stack");}elements.pop_back();
}template <typename T>
T Stack<T>::top() const {if (elements.empty()) {throw out_of_range("Stack<>::top(): empty stack");}return elements.back();
}
上面的代码定义了一个类模板,它表示一个堆栈数据结构。当类模板被实例化时,编译器会根据传递的类型参数推断出T的实际类型。
例如,以下代码将创建一个堆栈,其中元素的类型为int:
Stack<int> myStack;
可以使用类模板来实现一个通用的容器类,如vector、list和map等。
template<typename T>
class MyContainer {public:MyContainer(int size);~MyContainer();void add(T value);T get(int index);private:T* data;int size;int count;
};template<typename T>
MyContainer<T>::MyContainer(int size) {this->data = new T[size];this->size = size;this->count = 0;
}template<typename T>
MyContainer<T>::~MyContainer() {delete[] this->data;
}template<typename T>
void MyContainer<T>::add(T value) {if (this->count >= this->size) {// expand container}this->data[this->count++] = value;
}template<typename T>
T MyContainer<T>::get(int index) {if (index < 0 || index >= this->count) {// handle out of bounds error}return this->data[index];
}
四、模板特化
模板特化是一种允许程序员为特定类型的模板参数提供特定实现的机制。也可以说模板特化是一种针对特定类型参数的特定实现。
例如,以下代码为Stack<int>提供了一个特化实现:
template <>
class Stack<int> {
private:vector<int> elements;
public:void push(int const&); void pop(); int top() const; bool empty() const {return elements.empty();}
};void Stack<int>::push(int const& element) {elements.push_back(element);
}void Stack<int>::pop() {if (elements.empty()) {throw out_of_range("Stack<int>::pop(): empty stack");}elements.pop_back();
}int Stack<int>::top() const {if (elements.empty()) {throw out_of_range("Stack<int>::top(): empty stack");}return elements.back();
}
上面的代码为Stack<int>提供了一个特化实现,其中元素的类型为int。注意,特化实现并不需要完全重写类模板,只需要提供需要特化的成员函数的实现即可。
使用模板特化来提供对某些类型的特定优化,以提高程序的性能。
五、模板参数
模板参数是程序员定义模板时可以传递的参数。它们可以是类型、整数值或指针类型。例如,以下代码定义了一个模板类,它有一个类型参数和一个整数参数:
template <typename T, int N>
class Array {
private:T elements[N];
public:T& operator[](int index) { if (index < 0 || index >= N) {throw out_of_range("Array::operator[](): index out of range");}return elements[index];}
};
上面的代码定义了一个模板类,它表示一个固定大小的数组。当类模板被实例化时,编译器会根据传递的参数推断出T的实际类型和N的实际值。例如,以下代码将创建一个大小为10的int数组:
Array<int, 10> myArray;
六、可变模板参数
可变模板参数允许模板接受任意数量的参数。例如,可以使用可变模板参数来实现一个通用的printf函数。
#include <iostream>
#include <sstream>template<typename... Args>
std::string format(const std::string& formatString, Args... args) {std::ostringstream oss;int argIndex = 0;size_t pos = 0;while (pos < formatString.size()) {size_t nextPos = formatString.find_first_of("%", pos);if (nextPos == std::string::npos) {oss << formatString.substr(pos);break;}oss << formatString.substr(pos, nextPos - pos);if (nextPos == formatString.size() - 1) {// handle error: incomplete format specifier}char formatChar = formatString[nextPos + 1];if (formatChar == '%') {oss << "%";} else {if (argIndex >= sizeof...(args)) {// handle error: too few arguments}if (formatChar == 'd') {oss << std::to_string(std::get<argIndex>(std::make_tuple(args...)));} else if (formatChar == 's){oss << std::get<argIndex>(std::make_tuple(args...));} else {// handle error: invalid format specifier}argIndex++;}pos = nextPos + 2;}if (argIndex < sizeof...(args)) {// handle error: too many arguments
}return oss.str();
}int main() {std::cout << format("%d + %d = %d, %s\n", 2, 3, 5, "hello world");return 0;
}
上述代码中,format函数的参数列表使用了可变模板参数Args,它表示任意数量的参数。在函数体内,我们使用std::make_tuple(args...)将所有参数打包成一个std::tuple对象,然后使用std::get<argIndex>来获取其中的某个参数,argIndex表示当前获取的参数在参数列表中的索引。 在函数体内,我们还使用了std::ostringstream来将所有参数转换为字符串,并使用了一些C++11特性,例如auto、decltype和std::initializer_list。
七、模板元编程
模板元编程是一种利用C++模板机制实现编译时计算的技术。它允许程序员在编译时生成代码,以便在运行时获得更好的性能。例如,以下代码使用模板元编程实现斐波那契数列的计算:
template <int N>
struct Fibonacci {static const int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};template <>
struct Fibonacci<0> {static const int value = 0;
};template <>
struct Fibonacci<1> {static const int value = 1;
};
上面的代码定义了一个模板类,它表示斐波那契数列中的第N个数字。当类模板被实例化时,编译器会递归地计算Fibonacci<N-1>::value和Fibonacci<N-2>::value,直到计算到Fibonacci<0>或Fibonacci<1>为止。这种技术可以在编译时生成高效的代码,以便在运行时获得更好的性能。
八、嵌套模板
嵌套模板是指在一个模板类或函数中使用另一个模板类或函数作为其成员。例如,可以使用嵌套模板来实现一个通用的二叉树数据结构,其中每个节点都存储一个键和一个值。
template<typename K, typename V>
class BinaryTree {private:struct Node {K key;V value;Node* left;Node* right;Node(K key, V value) : key(key), value(value), left(nullptr), right(nullptr) {}};Node* root;public:BinaryTree() : root(nullptr) {}void insert(K key, V value);V find(K key);
};template<typename K, typename V>
void BinaryTree<K, V>::insert(K key, V value) {Node** node = &root;while (*node != nullptr) {if (key < (*node)->key) {node = &((*node)->left);} else if (key > (*node)->key) {node = &((*node)->right);} else {// handle duplicate key error}}*node = new Node(key, value);
}template<typename K, typename V>
V BinaryTree<K, V>::find(K key) {Node* node = root;while (node != nullptr) {if (key < node->key) {node = node->left;} else if (key > node->key) {node = node->right;} else {return node->value;}}// handle key not found error
}
九、注意事项
使用C++模板时需要注意以下几点:
-
代码可读性:使用模板可以提高代码的通用性,但是也可能导致代码的可读性下降。如果不小心过度使用模板,代码可能会变得难以阅读和理解。因此,应该在使用模板时保持适度,只在必要的情况下使用它们。
-
代码复杂性:模板代码可能会变得非常复杂,因为它们需要支持多种不同的类型和值。这可能会导致编译时间和二进制文件大小的增加。为了避免这种情况,应该尽可能简化模板代码,并且在需要使用模板的地方进行分离编译。
-
模板特化和重载:当为特定类型的参数提供特定的实现时,需要注意模板特化和重载的使用。如果模板特化和重载不正确使用,可能会导致编译错误或运行时错误。
-
模板参数的类型限制:模板参数的类型限制可以确保模板只能用于特定类型的参数。这可以帮助避免在使用模板时出现类型错误。如果没有类型限制,可能会导致编译错误或运行时错误。
-
编译错误:在使用模板时,可能会遇到许多编译错误。这些错误可能很难调试,因为它们通常发生在编译期间。这就是有些公司禁止程序员使用模板的原因。为了避免这种情况,应该仔细检查所有的错误消息,并尝试使用一些工具来调试代码。
总之,在使用C++模板时,需要权衡代码的通用性和可读性,以及代码的复杂性和性能。同时,需要遵循模板参数类型限制和模板特化和重载的规则,以避免在使用模板时出现错误。
相关文章:
C++基础知识【8】模板
目录 一、什么是C模板? 二、函数模板 三、类模板 四、模板特化 五、模板参数 六、可变模板参数 七、模板元编程 八、嵌套模板 九、注意事项 一、什么是C模板? C模板是C编程中非常重要的一部分,它允许程序员以一种通用的方式编写代码…...

MAC-安装Java环境、JDK配置、IDEA插件推荐
背景:发现经常换电脑装环境等比较麻烦,主要还是想记录一下,不要每次安装都到处翻。。 1、下载并安装JDK 到官网下载所需的JDK:https://www.oracle.com/technetwork/java/javase/downloads/jdk11-downloads-5066655.html 这儿下…...
Mysql如何避免常见的索引失效
Mysql索引算是非常常用了,用得好提高效率,用的不好适得其反 如何避免常见的索引失效 1.模糊查询 使用 LIKE 查询时,如果搜索表达式以通配符开头,如 %value,MySQL 就无法使用索引来加速查询,因为它无法倒序…...

SpringBoot集成Redis及问题解决
SpringBoot集成Redis 此篇文章为SpringBoot集成Redis的简单介绍,依赖、序列化操作、工具类都可以在后面的实操中直接搬运使用或者在此基础上进行改进使用 1、集成Redis 1.1、新建SpringBoot项目 新建项目这边就不一一介绍了,大家如果还有不会的可以自行…...
PyTorch 人工智能研讨会:6~7
原文:The Deep Learning with PyTorch Workshop 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只关心…...

AI绘图设计师Stable Diffusion成为生产力工具(五):放大并修复老照片、马赛克照片、身份证件照
S:你安装stable diffusion就是为了看小姐姐么? I :当然不是,当然是为了公司的发展谋出路~~ 预先学习: 安装webui《Windows安装Stable Diffusion WebUI及问题解决记录》。运行使用时问题《Windows使用Stable Diffusion时…...
cubase正版下载安装包-cubase正版下载v1.2.0.69 软件激活版
cubase正版下载是一款实用的音乐创作类软件。我们可以通过这款软件实现创作音乐的自由,再也不用花大价钱请别人来帮忙制作,只需自己动动手就可以轻松完成我们所想要的,这款软件做到了让每一位热爱音乐的人都可以实现自己的梦想。 cubase正版…...
Python机器学习:支持向量机
这是我读本科的时候第一个接触到的机器学习算法,但也是第一个听完就忘的。。。 他的基本思想很简单:想办法把一个样本集划成两个部分:对于空间中的样本点集合,我们找到一个超平面把这个样本点集合给分成两个部分,其中…...

矩阵和线性代数的应用
矩阵和线性代数是数学中重要的概念,它们被广泛应用于物理、工程、计算机科学、经济学等众多领域。本文将讨论矩阵和线性代数的一些基本概念以及它们在实际应用中的重要性和影响。 一、矩阵和线性代数的基本概念 矩阵是由数字组成的矩形数组。它可以表示线性方程组…...

六:内存回收
内存回收: 应用程序通过 malloc 函数申请内存的时候,实际上申请的是虚拟内存,此时并不会分配物理内存。 当应用程序读写了这块虚拟内存,CPU 就会去访问这个虚拟内存, 这时会发现这个虚拟内存没有映射到物理内存&…...

【cpolar 内网穿透】Openwrt 软路由实现内网穿透
cpolar 是一种安全的内网穿透云服务,它将内网下的本地服务器通过安全隧道暴露至公网。使得公网用户可以正常访问内网服务。 文章目录 前言一、上传 cpolar 安装包二、配置cpolar环境变量三、安装并配置 cpolar 服务3.1 安装 cpolar3.2 启动 cpolar3.3 进行其他配置 …...
Android 10.0 Camera2 拍照功能默认选前摄像头
1.概述 在10.0的系统产品开发中,对于app调用系统api来打开摄像头拍照的功能也是常有的功能,而拍照一般是默认打开后置摄像头拍照的,由于 客户的产品特殊要求,需要打开前置摄像头拍照功能,所以需要了解拍照功能的流程,然后修改默认前置摄像头打开拍照功能就可以了 app调…...

vue-vue2和vue3的diff算法
核心要点 数据变化时,vue如何更新节点虚拟DOM 和 真实DOM 的区别vue2 diff 算法vue3 diff 算法 一、 数据变化时,vue如何更新节点 首先渲染真实DOM的开销是很大,比如有时候我们修改了某个数据且修改的数据量很大时,此时会频繁的…...

一文解读基于PaddleSeg的钢筋长度超限监控方案
项目背景 钢铁厂生产钢筋的过程中会存在部分钢筋长度超限的问题,如果不进行处理,容易造成机械臂损伤。因此,需要通过质检流程,筛选出存在长度超限问题的钢筋批次,并进行预警。传统的处理方式是人工核查,该方…...

NumPy 数组学习手册:1~5
原文:Learning NumPy Array 协议:CC BY-NC-SA 4.0 译者:飞龙 一、NumPy 入门 让我们开始吧。 我们将在不同的操作系统上安装 NumPy 和相关软件,并查看一些使用 NumPy 的简单代码。 正如“序言”所述,SciPy 与 NumPy 密…...

【C++11】晦涩难懂语法系列:可变参数模板
目录 可变参数模板 1.1 概念 1.2 可变参数模板定义 1.3 参数包的展开方式 1.3.1 递归展开参数包 1.3.2 逗号表达式展开参数包 1.4 STL的emplace系列函数 可变参数模板 1.1 概念 在C语言阶段,我们已经接触过可变参数,比如scand、printf等等 这里…...

计算机组成原理第二章——数据的表示与运算(下)
提示:时光清浅处 一步一安然 文章目录 前言2.3.1 浮点数的表示2.3.2 IEEE7542.2.3 浮点数的运算 前言 本节主要讲三个问题,浮点数的表示,IEEE 754标准,浮点数的加减运算 2.3.1 浮点数的表示 浮点数的作用和基本原理 定点数可表…...

1.mybatis-plus入门及使用
1.什么是MybatisPlus MyBatis-Plus 官网 为什么要学MybatisPlus? MybatisPlus可以节省大量时间,所有的CRUD代码都可以自动化完成MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效…...

JavaWeb开发 —— 前端工程化
目录 一、前后端分离开发 二、YApi 三、前端工程化 1. 环境准备:vue-cli 2. Vue项目创建 四、Vue项目开发流程 一、前后端分离开发 ① 最早的前端开发就是实现页面,顶多再写写JS让页面可以有交互的特效。属于前后端未分离的时代。 早期前后端混合开…...
listener监听器框架
监听器是Web开发中常用的一种组件,用于监听某些事件并根据事件触发相应的处理逻辑。在Spring Boot中使用监听器可以方便地实现对程序中各种事件的监听,比如启动事件、关闭事件等。 首先需要定义一个监听器,通常需要实现ApplicationListener接…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...