当前位置: 首页 > news >正文

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++模板时需要注意以下几点:

  1. 代码可读性:使用模板可以提高代码的通用性,但是也可能导致代码的可读性下降。如果不小心过度使用模板,代码可能会变得难以阅读和理解。因此,应该在使用模板时保持适度,只在必要的情况下使用它们。

  2. 代码复杂性:模板代码可能会变得非常复杂,因为它们需要支持多种不同的类型和值。这可能会导致编译时间和二进制文件大小的增加。为了避免这种情况,应该尽可能简化模板代码,并且在需要使用模板的地方进行分离编译。

  3. 模板特化和重载:当为特定类型的参数提供特定的实现时,需要注意模板特化和重载的使用。如果模板特化和重载不正确使用,可能会导致编译错误或运行时错误。

  4. 模板参数的类型限制:模板参数的类型限制可以确保模板只能用于特定类型的参数。这可以帮助避免在使用模板时出现类型错误。如果没有类型限制,可能会导致编译错误或运行时错误。

  5. 编译错误:在使用模板时,可能会遇到许多编译错误。这些错误可能很难调试,因为它们通常发生在编译期间。这就是有些公司禁止程序员使用模板的原因。为了避免这种情况,应该仔细检查所有的错误消息,并尝试使用一些工具来调试代码。

总之,在使用C++模板时,需要权衡代码的通用性和可读性,以及代码的复杂性和性能。同时,需要遵循模板参数类型限制和模板特化和重载的规则,以避免在使用模板时出现错误。

相关文章:

C++基础知识【8】模板

目录 一、什么是C模板&#xff1f; 二、函数模板 三、类模板 四、模板特化 五、模板参数 六、可变模板参数 七、模板元编程 八、嵌套模板 九、注意事项 一、什么是C模板&#xff1f; C模板是C编程中非常重要的一部分&#xff0c;它允许程序员以一种通用的方式编写代码…...

MAC-安装Java环境、JDK配置、IDEA插件推荐

背景&#xff1a;发现经常换电脑装环境等比较麻烦&#xff0c;主要还是想记录一下&#xff0c;不要每次安装都到处翻。。 1、下载并安装JDK 到官网下载所需的JDK&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk11-downloads-5066655.html 这儿下…...

Mysql如何避免常见的索引失效

Mysql索引算是非常常用了&#xff0c;用得好提高效率&#xff0c;用的不好适得其反 如何避免常见的索引失效 1.模糊查询 使用 LIKE 查询时&#xff0c;如果搜索表达式以通配符开头&#xff0c;如 %value&#xff0c;MySQL 就无法使用索引来加速查询&#xff0c;因为它无法倒序…...

SpringBoot集成Redis及问题解决

SpringBoot集成Redis 此篇文章为SpringBoot集成Redis的简单介绍&#xff0c;依赖、序列化操作、工具类都可以在后面的实操中直接搬运使用或者在此基础上进行改进使用 1、集成Redis 1.1、新建SpringBoot项目 新建项目这边就不一一介绍了&#xff0c;大家如果还有不会的可以自行…...

PyTorch 人工智能研讨会:6~7

原文&#xff1a;The Deep Learning with PyTorch Workshop 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关心…...

AI绘图设计师Stable Diffusion成为生产力工具(五):放大并修复老照片、马赛克照片、身份证件照

S&#xff1a;你安装stable diffusion就是为了看小姐姐么&#xff1f; I &#xff1a;当然不是&#xff0c;当然是为了公司的发展谋出路~~ 预先学习&#xff1a; 安装webui《Windows安装Stable Diffusion WebUI及问题解决记录》。运行使用时问题《Windows使用Stable Diffusion时…...

cubase正版下载安装包-cubase正版下载v1.2.0.69 软件激活版

cubase正版下载是一款实用的音乐创作类软件。我们可以通过这款软件实现创作音乐的自由&#xff0c;再也不用花大价钱请别人来帮忙制作&#xff0c;只需自己动动手就可以轻松完成我们所想要的&#xff0c;这款软件做到了让每一位热爱音乐的人都可以实现自己的梦想。 cubase正版…...

Python机器学习:支持向量机

这是我读本科的时候第一个接触到的机器学习算法&#xff0c;但也是第一个听完就忘的。。。 他的基本思想很简单&#xff1a;想办法把一个样本集划成两个部分&#xff1a;对于空间中的样本点集合&#xff0c;我们找到一个超平面把这个样本点集合给分成两个部分&#xff0c;其中…...

矩阵和线性代数的应用

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

六:内存回收

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

【cpolar 内网穿透】Openwrt 软路由实现内网穿透

cpolar 是一种安全的内网穿透云服务&#xff0c;它将内网下的本地服务器通过安全隧道暴露至公网。使得公网用户可以正常访问内网服务。 文章目录 前言一、上传 cpolar 安装包二、配置cpolar环境变量三、安装并配置 cpolar 服务3.1 安装 cpolar3.2 启动 cpolar3.3 进行其他配置 …...

Android 10.0 Camera2 拍照功能默认选前摄像头

1.概述 在10.0的系统产品开发中,对于app调用系统api来打开摄像头拍照的功能也是常有的功能,而拍照一般是默认打开后置摄像头拍照的,由于 客户的产品特殊要求,需要打开前置摄像头拍照功能,所以需要了解拍照功能的流程,然后修改默认前置摄像头打开拍照功能就可以了 app调…...

vue-vue2和vue3的diff算法

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

一文解读基于PaddleSeg的钢筋长度超限监控方案

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

NumPy 数组学习手册:1~5

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

【C++11】晦涩难懂语法系列:可变参数模板

目录 可变参数模板 1.1 概念 1.2 可变参数模板定义 1.3 参数包的展开方式 1.3.1 递归展开参数包 1.3.2 逗号表达式展开参数包 1.4 STL的emplace系列函数 可变参数模板 1.1 概念 在C语言阶段&#xff0c;我们已经接触过可变参数&#xff0c;比如scand、printf等等 这里…...

计算机组成原理第二章——数据的表示与运算(下)

提示&#xff1a;时光清浅处 一步一安然 文章目录 前言2.3.1 浮点数的表示2.3.2 IEEE7542.2.3 浮点数的运算 前言 本节主要讲三个问题&#xff0c;浮点数的表示&#xff0c;IEEE 754标准&#xff0c;浮点数的加减运算 2.3.1 浮点数的表示 浮点数的作用和基本原理 定点数可表…...

1.mybatis-plus入门及使用

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

JavaWeb开发 —— 前端工程化

目录 一、前后端分离开发 二、YApi 三、前端工程化 1. 环境准备&#xff1a;vue-cli 2. Vue项目创建 四、Vue项目开发流程 一、前后端分离开发 ① 最早的前端开发就是实现页面&#xff0c;顶多再写写JS让页面可以有交互的特效。属于前后端未分离的时代。 早期前后端混合开…...

listener监听器框架

监听器是Web开发中常用的一种组件&#xff0c;用于监听某些事件并根据事件触发相应的处理逻辑。在Spring Boot中使用监听器可以方便地实现对程序中各种事件的监听&#xff0c;比如启动事件、关闭事件等。 首先需要定义一个监听器&#xff0c;通常需要实现ApplicationListener接…...

tp5实现导入excel表到数据库

hello&#xff0c;大家好&#xff0c;好长时间没有更新文章了。最近一直在忙着做项目。所以断更了。 那么好&#xff0c;各位老铁是否想要实现导入导出的功能 请关注我&#xff0c;解密如何实现导入导出&#xff0c; 那么今天先来讲一下用thinkphp5.0 如何实现Excel表格导入数据…...

Python基础-04 字符串

字符串的表示方式 在Python中,可以使用一对单引号/双引号或者一对三个双引号/一对三个单引号表示字符串 a hello b "hello" c hello d """hello""" # 如果字符串里面还有双引号,外面就可以使用单引号 # 反之一样 # 如果字符串里…...

VVC之编码结构

VVC之编码结构&#xff08;新一代通用视频编码的读书笔记&#xff09; 缩写概述EncAppmain函数解读 缩写 缩写含义CVSCoded Video Sequence, 编码视频序列IRAPIntra Random Access Point, 帧内随机接入点GDRGradual Decoding Refresh, 逐渐解码刷新AUAccess Unit, 访问单元PUP…...

FPGA基于SFP光口实现10G万兆网UDP通信 10G Ethernet Subsystem替代网络PHY芯片 提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、详细设计方案4、vivado工程详解5、上板调试验证并演示6、福利&#xff1a;工程代码的获取 1、前言 目前网上的fpga实现udp基本生态如下&#xff1a; 1&#xff1a;verilog编写的udp收发器&#xff0c;但不带ping功能&#xff0c;这样的代…...

Linux Redis主从复制 | 哨兵监控模式 | 集群搭建 | 超详细

Linux Redis主从复制 | 哨兵监控模式 | 集群搭建 | 超详细 一 Redis的主从复制二 主从复制的作用三 主从复制的流程四 主从复制实验4.1 环境部署4.2 安装Redis&#xff08;主从服务器&#xff09;4.3 修改Master节点Redis配置文件 (192.168.163.100)4.4 修改Slave节点Redis配置…...

整柜海运到美国的规格和收费标准是什么

整柜海运是指将所有货物安装在一个整箱内&#xff0c;由发货人和收货人共同操作&#xff0c;而目的港的收货人一般只有一个&#xff0c;方便操作。整柜海运到美国的主要流程有以下几个步骤&#xff1a;订舱、装柜、报关、海运、清关、提柜和送货。实际上&#xff0c;国际物流出…...

Session和Cookie区别介绍+面试题

Session 会话&#xff1a; 对应的英文单词&#xff1a;session用户打开浏览器&#xff0c;进行一系列操作&#xff0c;然后关闭浏览器。整个过程叫做一次会话一个会话包含多次请求 session机制属于B/S结构的一部分&#xff0c;主要的作用就是为了保存会话状态。(用户登录成功后…...

easyx

普通的画线图什么的 首先我们需要安装一个easyx的图形库&#xff0c;然后把头文件搞出来 #include <stdio.h> #include <easyx.h>//easyx画线啥啥的图形库 #include <graphics.h> #include <math.h> #include <conio.h>//键盘操作的头文件 设…...

记一次科学

华为云与Centos8 华为云99元Hongkong的服务器&#xff1a;1M&#xff0c;1C&#xff0c;2G&#xff0c;40G&#xff0c;自带不可更改的Centos 8.2 64bit 华为yum源不可以&#xff0c;网上找了可用的CentOS8 官方源不支持后配置yum源 # 备份 mv /etc/yum.repos.d/CentOS-Base…...

亚马逊被人差评了怎么办?

第一种&#xff1a; 也是最简单的做法就是通过电话或者邮件联系留差评的买家&#xff0c;大致意思就是按照货值的2-3倍作为赔偿&#xff0c;能不能把差评给删了 赔偿一个普通产品2-3倍的价格比起找服务商删一个差评几百到一千不等可以说是绰绰有余了&#xff0c;碰到那种愿意…...