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接…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...

轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...

Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...