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

C++模板相关概念汇总

文章目录

      • 一、模板的概念与作用
      • 二、函数模板
        • 模板的非类型参数
        • 调用顺序
      • 三、类模板
      • 四、模板的编译模型

一、模板的概念与作用

C++模板是一种强大的代码复用机制,它允许程序员编写通用的代码,能够处理不同类型的数据,而无需为每种类型都重复编写相似的代码。通过模板,可以在编译时根据具体的类型参数生成对应的代码,极大地提高了代码的灵活性和可复用性。

例如,不用模板的情况下,如果要编写一个交换两个整数的函数和交换两个浮点数的函数,代码可能如下:

// 交换两个整数的函数
void swapInt(int& a, int& b) {int temp = a;a = b;b = temp;
}// 交换两个浮点数的函数
void swapFloat(float& a, float& b) {float temp = a;a = b;b = temp;
}

而使用模板,就可以编写一个通用的交换函数,能适用于多种类型:

// 模板函数
template <typename T>
void swap(T& a, T& b) {T temp = a;a = b;b = temp;
}

二、函数模板

  1. 定义
    函数模板以 template 关键字开头,后面跟着用尖括号括起来的模板参数列表,通常是类型参数(用 typenameclass 关键字来声明,表示此处可以是任意类型)。例如上面的 swap 函数模板,template <typename T> 就是定义部分,T 就是类型参数,之后定义的函数体中使用了这个 T 类型来处理参数,使得该函数可以针对不同类型进行实例化。

函数模板不进行编译,在调用点必须要能看见模板定义,否则将产生链接错误,模板调用点根据类型会实例化一份函数进行编译

  1. 实例化
    函数模板本身并不会生成实际的代码,只有在被调用时,编译器根据传入的实际参数类型来实例化模板,生成对应的具体函数版本。例如:
int num1 = 10, num2 = 20;
swap(num1, num2);  // 此时编译器会根据int类型实例化出一个专门用于交换两个整数的swap函数版本float num3 = 3.14f, num4 = 2.71f;
swap(num3, num4);  // 同样,会根据float类型实例化出交换两个浮点数的swap函数版本

编译器会在后台为每一次不同类型的调用生成相应的函数代码,这个过程对程序员来说基本是透明的,但需要注意的是,如果实例化过程中出现类型相关的错误(比如类型不支持某些操作等),编译器会在编译阶段报错。

#include<iostream>
using namespace std;//函数模板   T E 模板的类型参数
template<typename  T , typename E>
void compare(T a, E b)
{cout << a << endl;
}
//显示实例化
template void compare<int, double>(int ,double);
int main()
{//隐式实例化compare(1,1);//模板的实参推演return 0;
}

模板参数不是一个简单的宏替换,而是重定义的过程 ,不是# define 而是typedef 的过程

老的编译器只检查函数头部是否符合语法规则,不检查函数体是否符合语法,但是新的编译器开始全部检查

函数模板不允许部分特例化

模板的非类型参数
#include<iostream>
using namespace std;//函数模板    SIZE:模板的非类型参数,是常量
template<typename  T , int SIZE>
int  findval(T *arr, T val)
{for (int i = 0; i < SIZE; ++i){if (val == arr[i]){cout << "find it" << endl;return i;}}return -1;
}
int main()
{int arr[] = {1,2,3,4};findval<int, 4>(arr, 2);//int lenth = sizeof(arr) / sizeof(arr[0]);findval<int, lenth>(arr, 2);//报错,模板的非类型参数必须是常量return 0;
}
//以上代码改成 const  int lenth = sizeof(arr) / sizeof(arr[0]);即可
  1. 模板参数推断
    在很多情况下,编译器可以根据函数调用时传入的实际参数自动推断出模板参数的类型,不需要显式指定。例如上面的 swap 函数调用,传入 int 类型参数时,编译器就能推断出 Tint 类型。但也有一些情况需要显式指定模板参数类型,比如:
template <typename T>
T add(T a, T b) {return a + b;
}int result = add(10, 20);  // 编译器可推断出T为int类型
double result2 = add<double>(10.0, 20.0);  // 这里显式指定模板参数为double类型,因为编译器可能无法准确从整数10和20推断出要用于double类型的加法运算
  1. 重载与特化
    函数模板可以重载,就像普通函数一样,只要函数签名(包括模板参数等)不同即可。例如:
template <typename T>
void print(T value) {std::cout << value << std=""};template <typename T>
void print(T* value) {std::cout << "Pointer: " << value << std::endl;
}

上述代码定义了两个 print 函数模板,一个用于打印普通类型的值,一个用于打印指针类型的值,根据传入参数的类型不同,编译器会选择合适的重载版本进行实例化。

此外,还可以对函数模板进行特化,即针对特定的类型提供专门的实现,当使用该特定类型调用模板函数时,就会采用特化的版本。例如:

template <typename T>
void compare(T a, T b) {std::cout << (a == b? "Equal" : "Not equal") << std::endl;
}// 针对char*类型的特化
template <>
void compare<char*>(char* a, char* b) {std::cout << (strcmp(a, b) == 0? "Equal" : "Not equal") << std::endl;
}

当调用 compare 函数模板时,如果传入的参数是 char* 类型,就会使用特化后的版本进行比较,而不是通用的版本。

调用顺序

先调用普通函数
在调用特例化
从函数模板实例化一个

三、类模板

  1. 定义
    类模板的定义方式与函数模板类似,也是以 template 关键字开头,后跟模板参数列表。例如:
template <typename T>
class Stack {
private:std::vector<T> elements;
public:void push(T element) {elements.push_back(element);}T pop() {T last = elements.back();elements.pop_back();return last;}
};

上述代码定义了一个简单的 Stack 类模板,它可以用来创建存储不同类型元素的栈结构,其中 T 就是类型参数,类中的成员变量和成员函数都基于这个 T 类型进行定义。

  1. 实例化
    类模板同样需要实例化才能生成实际的类对象。实例化可以通过在使用类模板时指定类型参数来实现,例如:
Stack<int> intStack;  // 实例化出一个存储int类型元素的栈
Stack<double> doubleStack;  // 实例化出一个存储double类型元素的栈intStack.push(10);
doubleStack.push(3.14);

和函数模板一样,编译器会根据指定的类型参数为每个实例化生成对应的类代码,包括类的成员变量的内存布局以及成员函数的具体实现等都会根据具体类型来确定。

  1. 模板参数
    类模板的模板参数除了常见的类型参数(用 typenameclass 声明)外,还可以有非类型参数,非类型参数通常是一些常量表达式,比如整数、枚举值、指针类型等。例如:
template <typename T, int size>
class Array {
private:T elements[size];
public:T& operator[](int index) {return elements[index];}
};

在上述 Array 类模板中,T 是类型参数,用于指定数组中元素的类型,而 int size 是一个非类型参数,用于指定数组的大小,这样就可以创建不同大小、不同元素类型的数组类对象,例如:

Array<int, 5> intArray;  // 创建一个存储5个int元素的数组类对象
Array<double, 10> doubleArray;  // 创建一个存储10个double元素的数组类对象
  1. 成员函数的定义
    类模板的成员函数可以在类内定义,也可以在类外定义。如果在类外定义,需要使用模板声明,并且要带上模板参数,例如:
template <typename T>
class MyClass {
public:void func();
};template <typename T>
void MyClass<T>::func() {// 函数体具体实现
}

注意,类模板的成员函数只有在被调用且对应的类模板实例化后才会被实例化生成实际的代码。

  1. 继承与模板
    在涉及继承关系时,模板类可以作为基类或者派生类。例如:
template <typename T>
class Base {// 基类的定义
};template <typename T>
class Derived : public Base<T> {// 派生类的定义,继承自Base<T>,保证类型的一致性
};

派生类在继承模板类时,需要正确指定模板参数,使得继承关系在类型上是匹配的,并且派生类可以根据自身需求扩展或修改基类的功能。

四、模板的编译模型

C++模板采用的是分离编译模型,也就是模板的定义和使用通常可以在不同的源文件中进行。但这也带来了一些问题,因为模板在编译时需要根据具体类型进行实例化,所以编译器在处理模板时,需要能看到模板的完整定义才能正确进行实例化生成代码。

例如,在一个源文件中定义了函数模板:

// file1.cpp
template <typename T>
void myTemplateFunction(T a) {// 函数体内容
}

在另一个源文件中使用这个模板函数:

// file2.cpp
#include <iostream>void func() {int num = 10;myTemplateFunction(num);  // 此处使用模板函数,但编译器可能无法正确实例化,因为看不到模板函数的完整定义
}

为了解决这个问题,常见的做法有将模板的定义和声明都放在头文件中(尽管违背了通常头文件放声明、源文件放定义的原则),或者使用 export 关键字(不过在现代C++中这个关键字已经很少使用且很多编译器支持有限)来告诉编译器模板的定义可以在其他地方找到,以便正确进行实例化。

总之,C++模板是一种非常强大且灵活的编程工具,通过合理运用它,可以写出高效、复用性强的代码,但同时也需要深入理解其语法、实例化机制以及编译相关的特点等内容,才能更好地驾驭它。

相关文章:

C++模板相关概念汇总

文章目录 一、模板的概念与作用二、函数模板模板的非类型参数调用顺序 三、类模板四、模板的编译模型 一、模板的概念与作用 C模板是一种强大的代码复用机制&#xff0c;它允许程序员编写通用的代码&#xff0c;能够处理不同类型的数据&#xff0c;而无需为每种类型都重复编写…...

MYSQL------sql基础

SQL基础与简介 定义&#xff1a;SQL即结构化查询语言&#xff08;Structured Query Language&#xff09;&#xff0c;是一种特殊目的的编程语言&#xff0c;用于存取数据以及查询、更新和管理关系数据库系统。作用&#xff1a;可以用于数据库的创建、数据的插入、查询、更新和…...

React Router 用法概览

React Router 用法 React 使得开发者能够轻松地创建交互式的单页应用&#xff08;SPA&#xff09;&#xff0c;单页应用的一个常见挑战是如何处理页面导航和路由吗&#xff0c;React Router 就是解决这个问题的工具 路由&#xff08;Router&#xff09;是 React Router 的核心…...

网络安全之高防IP的实时监控精准防护

高防IP是一种网络安全设备&#xff0c;用于保护网络服务不受到各类攻击的影响&#xff0c;确保业务的持续稳定运行。它通过监控、识别和封锁恶意攻击流量&#xff0c;提供高级别的防护&#xff0c;降低业务被攻击的风险&#xff0c;并提升网络的稳定性和可靠性。 一、实时监控的…...

2024年中国新能源汽车用车发展怎么样 PaperGPT(二)

用车趋势深入分析 接上文&#xff0c;2024年中国新能源汽车用车发展怎么样 PaperGPT&#xff08;一&#xff09;-CSDN博客本文将继续深入探讨新能源汽车的用车强度、充电行为以及充电设施的现状。 用车强度 月均行驶里程&#xff1a;2024年纯电车辆月均行驶超过1500公里&…...

LINUXC 时间相关操作

文章目录 时间戳获取本地时间struct tm 结构体高精度的时间struct timeval 结构体相关函数time()localtime()gmtime()gettimeofday()strftime()mktime() 示例代码 时间戳 时间戳是指计算机中存储的数字型时间。它是以一个特定的时间点作为起点&#xff08;通常是1970年1月1日0…...

网络游戏之害

网络游戏之害&#xff1a; 网络游戏于今之世风靡四方&#xff0c;其娱人耳目、畅人心怀之效&#xff0c;固为人知&#xff0c;然所藏之害&#xff0c;若隐伏之暗潮&#xff0c;汹涌而至时&#xff0c;足以覆舟&#xff0c;尤以青年为甚&#xff0c;今且缕析其害&#xff0c;以…...

SpringMVC的消息转换器

SpringMVC的消息转换器&#xff08;Message Converter&#xff09;是Spring框架中用于处理HTTP请求和响应体与Java对象之间转换的组件。它们使得开发人员可以轻松地将HTTP请求的数据映射到方法参数&#xff0c;并将返回的对象转换为HTTP响应。 工作原理 当一个HTTP请求到达Spr…...

Chrome 浏览器下载安装教程,保姆级教程

大家好&#xff0c;今天我们来聊一聊如何在国内下载和安装最新版本的 Chrome 浏览器。由于众所周知的原因&#xff0c;Google 的网站在国内是被屏蔽的&#xff0c;因此很多朋友在下载 Chrome 浏览器 时会遇到困难。其实&#xff0c;不必担心&#xff0c;今天我将为大家带来一份…...

ElasticSearch系列(一)

一.了解ES、倒排索引、es的一些概念、安装es、kibana 二.DSL&#xff1b;索引库操作 三.Java RestClient&#xff1a;索引库操作 一、了解ES、倒排索引、es的一些概念、安装es、kibana kibana、logstash、beats Elasticserach 存储&#xff0c;计算 &#xff0c;搜索数据 –…...

C++技巧:map和vector

一&#xff0c;map是有序的&#xff0c;unordered_map是无序的 在C中&#xff0c;std::map 和 std::unordered_map 是两种不同的容器&#xff0c;它们都用于存储键值对&#xff0c;但是它们的底层实现和性能特性有所不同。以下是它们的详细介绍&#xff1a; std::map std::m…...

中建海龙:科技助力福城南产业片区绿色建筑发展

在快速发展的城市化进程中&#xff0c;绿色建筑以其环保、节能、可持续的特点日益受到重视。作为建筑工业化领域的领军企业&#xff0c;中建海龙科技有限公司&#xff08;简称“中建海龙”&#xff09;凭借其卓越的科技实力和创新举措&#xff0c;在推动绿色建筑发展方面做出了…...

模块化通讯管理机在物联网系统中的应用

安科瑞刘鸿鹏 摘要 随着能源结构转型和智能化电网的推进&#xff0c;电力物联网逐渐成为智能电网的重要组成部分。本文以安科瑞ANet系列智能通信管理机为例&#xff0c;探讨其在电力物联网中的应用&#xff0c;包括数据采集、规约转换、边缘计算、远程控制等技术实践&#…...

建立一个Macos载入image的实例含界面

前言 为了方便ios程序的开发&#xff0c;有时候需要先用的Macos平台进行一些功能性的程序开发。 作为对比和参考。 1、创建一个MacOS的App 2、主界面控件的增加 添加的控件方法与ios相同&#xff0c;也是再用commandshiftL&#xff08;CtrlShiftL&#xff09;,就会弹出控件…...

Redis List列表

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Redis List列表 收录于专栏[redis] 本专栏旨在分享学习Redis的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 概述 常用命令 LPUSH …...

继承与多态 - 继承机制、虚函数、纯虚函数

引言 C 是一种支持面向对象编程&#xff08;OOP&#xff09;的编程语言&#xff0c;继承和多态是 OOP 的两个核心概念。通过继承&#xff0c;我们可以创建新的类&#xff0c;这些新类可以重用现有类的代码&#xff0c;并且可以根据需要进行扩展或修改。多态则允许我们编写更加…...

【QT】C++线程安全的单例模板

模板代码 #pragma once #include <mutex> #include <atomic>// CRTP基类模板 Curiously Recurring Template Parttern—奇异递归模板模式。 template <typename T> class SingletonCRTP { public:// 禁止拷贝构造和赋值操作SingletonCRTP(const SingletonCR…...

node.js内置模块之---EventEmitter 类

EventEmitter 类什么作用 EventEmitter 类的主要方法 EventEmitter 类什么作用 在 Node.js 中&#xff0c;EventEmitter 是一个非常核心的类&#xff0c;它提供了一种事件驱动的机制。几乎所有的 Node.js 核心模块&#xff08;如 fs, http, net 等&#xff09;都采用了事件驱…...

SWM221系列芯片之电机应用及控制

经过对SWM221系列的强大性能及外设资源&#xff0c;TFTLCD彩屏显示及控制进行了整体介绍后&#xff0c;新迎来我们的电控篇---SWM221系列芯片之电机应用及控制。在微控制器市场面临性能、集成度与成本挑战的当下&#xff0c;SWM221系列芯片以其卓越性能与创新设计&#xff0c;受…...

单片机-静动态数码管实验

P0控制数码管 &#xff0c;P0低电平 P1,P2,P3高电平 1、静态数码管 需求&#xff1a;数码管显示0&#xff0c;即让p0端口输出数字0的段码0x3f(共阴) #include "reg52.h" typedef unsigned int u16; typedef unsigned char u8; //数码管显示数字的数组 共阴极 …...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...