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接…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
