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接…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
