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

C++中的模板

1.模板

C++ 的模板一直是这门语言的一种特殊的艺术,模板甚至可以独立作为一门新的语言来进行使用。模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理,仅在运行时处理那些最核心的动态服务,进而大幅优化运行期的性能。因此模板也被很多人视作 C++ 的黑魔法之一。

1.1外部模板

传统 C++ 中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板的实例化。

为此,C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使我们能够显式的通知编译器何时进行模板的实例化:

template class std::vector<bool>;          // 强行实例化
extern template class std::vector<double>; // 不在该当前编译文件中实例化模板

1.2 尖括号 ">"

在传统 C++ 的编译器中,>>一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:

std::vector<std::vector<int>> matrix;

这在传统 C++ 编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于像下面这种写法都能够通过编译:

template<bool T>
class MagicType {bool magic = T;
};
// in main function:
std::vector<MagicType<(1>2)>> magic; // 合法, 但不建议写出这样的代码

1.3类型别名模板

在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:模板是用来产生类型的。在传统 C++ 中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:

template<typename T, typename U>
class MagicType {
public:T dark;U magic;
};
// 不合法
template<typename T>
typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;

C++11 使用 using 引入了下面这种形式的写法,并且同时支持对传统 typedef 相同的功效:

通常我们使用 typedef 定义别名的语法是:typedef 原名称 新名称;,但是对函数指针等别名的定义语法却不相同,这通常给直接阅读造成了一定程度的困难。

typedef int (*process)(void *);
using NewProcess = int(*)(void *);
template<typename T>
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;
int main() {TrueDarkMagic<bool> you;
}

1.4变长参数模板

模板一直是 C++ 所独有的黑魔法(一起念:Dark Magic)之一。 在 C++11 之前,无论是类模板还是函数模板,都只能按其指定的样子, 接受一组固定数量的模板参数;而 C++11 加入了新的表示方法, 允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定。

template<typename... Ts> class Magic;

模板类 Magic 的对象,能够接受不受限制个数的 typename 作为模板的形式参数,例如下面的定义:

class Magic<int,std::vector<int>,std::map<std::string,std::vector<int>>> darkMagic;

既然是任意形式,所以个数为 0 的模板参数也是可以的:class Magic<> nothing;。

如果不希望产生的模板参数个数为 0,可以手动的定义至少一个模板参数:

template<typename Require, typename... Args> class Magic;

变长参数模板也能被直接调整到到模板函数上。传统 C 中的 printf 函数, 虽然也能达成不定个数的形参的调用,但其并非类别安全。 而 C++11 除了能定义类别安全的变长参数函数外, 还可以使类似 printf 的函数能自然地处理非自带类别的对象。 除了在模板参数中能使用 ... 表示不定长模板参数外, 函数参数也使用同样的表示法代表不定长参数, 这也就为我们简单编写变长参数函数提供了便捷的手段,例如:

template<typename... Args> void printf(const std::string &str, Args... args);

那么我们定义了变长的模板参数,如何对参数进行解包呢?

首先,我们可以使用 sizeof... 来计算参数的个数,:

template<typename... Ts>
void magic(Ts... args) {std::cout << sizeof...(args) << std::endl;
}

我们可以传递任意个参数给 magic 函数:

magic(); // 输出0
magic(1); // 输出1
magic(1, ""); // 输出2

其次,对参数进行解包,到目前为止还没有一种简单的方法能够处理参数包,但有两种经典的处理手法:

1. 递归模板函数

递归是非常容易想到的一种手段,也是最经典的处理方法。这种方法不断递归地向函数传递模板参数,进而达到递归遍历所有模板参数的目的:

#include <iostream>
template<typename T0>
void printf1(T0 value) {std::cout << value << std::endl;
}
template<typename T, typename... Ts>
void printf1(T value, Ts... args) {std::cout << value << std::endl;printf1(args...);
}
int main() {printf1(1, 2, "123", 1.1);return 0;
}

2. 变参模板展开

你应该感受到了这很繁琐,在 C++17 中增加了变参模板展开的支持,于是你可以在一个函数中完成 printf 的编写:

template<typename T0, typename... T>
void printf2(T0 t0, T... t) {std::cout << t0 << std::endl;if constexpr (sizeof...(t) > 0) printf2(t...);
}

事实上,有时候我们虽然使用了变参模板,却不一定需要对参数做逐个遍历,我们可以利用 std::bind 及完美转发等特性实现对函数和参数的绑定,从而达到成功调用的目的。

3. 初始化列表展开

递归模板函数是一种标准的做法,但缺点显而易见的在于必须定义一个终止递归的函数。

这里介绍一种使用初始化列表展开的黑魔法:

template<typename T, typename... Ts>
auto printf3(T value, Ts... args) {std::cout << value << std::endl;(void) std::initializer_list<T>{([&args] {std::cout << args << std::endl;}(), value)...};
}

在这个代码中,额外使用了 C++11 中提供的初始化列表以及 Lambda 表达式的特性(下一节中将提到)。

通过初始化列表,(lambda 表达式, value)... 将会被展开。由于逗号表达式的出现,首先会执行前面的 lambda 表达式,完成参数的输出。 为了避免编译器警告,我们可以将 std::initializer_list 显式的转为 void。

1.5折叠表达式

C++ 17 中将变长参数这种特性进一步带给了表达式,考虑下面这个例子:

#include <iostream>
template<typename ... T>
auto sum(T ... t) {return (t + ...);
}
int main() {std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}

1.6非类型模板参数推导

前面我们主要提及的是模板参数的一种形式:类型模板参数。

template <typename T, typename U>
auto add(T t, U u) {return t+u;
}

其中模板的参数 T 和 U 为具体的类型。 但还有一种常见模板参数形式可以让不同字面量成为模板参数,即非类型模板参数:

template <typename T, int BufSize>
class buffer_t {
public:T& alloc();void free(T& item);
private:T data[BufSize];
}
buffer_t<int, 100> buf; // 100 作为模板参数

在这种模板参数形式下,我们可以将 100 作为模板的参数进行传递。 在 C++11 引入了类型推导这一特性后,我们会很自然的问,既然此处的模板参数 以具体的字面量进行传递,能否让编译器辅助我们进行类型推导, 通过使用占位符 auto 从而不再需要明确指明类型? 幸运的是,C++17 引入了这一特性,我们的确可以 auto 关键字,让编译器辅助完成具体类型的推导, 例如:

template <auto value> void foo() {std::cout << value << std::endl;return;
}
int main() {foo<10>();  // value 被推导为 int 类型
}

2.总结

模板对于C++程序的效率和可复用性有较大的帮助,但同时会降低代码的可读性,不建议过度使用模板,否则编写的代码可维护性较差。

相关文章:

C++中的模板

1.模板C 的模板一直是这门语言的一种特殊的艺术&#xff0c;模板甚至可以独立作为一门新的语言来进行使用。模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理&#xff0c;仅在运行时处理那些最核心的动态服务&#xff0c;进而大幅优化运行期的性能。因此模板也被…...

Maven_第四章 使用Maven:IDEA环境

目录第一节 创建父工程第二节 配置Maven信息第三节 创建Java模块工程第四节 创建Web模块工程1、创建模块2、修改打包方式3、Web 设定4、借助IDEA生成web.xml5、设置 Web 资源的根目录6、测试6.1 创建文件6.2 配置tomcat第五节 其他操作1、在IDEA中执行Maven命令①直接执行②手动…...

RocketMQ基础学习

前言&#xff1a; RocketMQ阿里开源的&#xff0c;一款分布式的消息中间件&#xff0c;它经过阿里的生产环境的高并发、高吞吐的考验&#xff0c;同时&#xff0c;还支持分布式事务等场景。RocketMQ使用Java语言进行开发&#xff0c;方便Java开发者学习源码。但是&#xff0c;R…...

I.MX6ULL内核开发4:设备号的组成与哈希表

目录 一、设备号 二、hash table 一、设备号 文件夹&#xff1a;/home/geralt/linux_driver/kernel/ebf_linux_kernel_6ull_depth1/include/linux/kdev.h 这里面是linux中关于设备号的具体描述 #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1)#defin…...

【博学谷学习记录】大数据课程-学习第六周总结

Hadoop 3.x的版本架构和模型介绍 由于Hadoop 2.0是基于JDK 1.7开发的&#xff0c;而JDK 1.7在2015年4月已停止更新&#xff0c;这直接迫使Hadoop社区基于JDK 1.8重新发布一个新的Hadoop版本&#xff0c;即hadoop 3.0。Hadoop 3.0中引入了一些重要的功能和优化&#xff0c;包括…...

Cordova

一、简介 Cordova 是用 Web 技术&#xff08; HTML&#xff0c;CSS 和 JS &#xff09;构建移动应用的平台。我们可以认为Cordova 是一个容器&#xff0c;用于将的 Web 应用移植到移动端&#xff0c;同时支持移动端的功能&#xff08;例如&#xff1a;定位、蓝牙、摄像头等&am…...

9.语义HTMLVScode扩展推荐

语义HTML 定义&#xff1a; 一个元素使用我们并不是只关心他是什么样子的&#xff0c;而是要去关心这个元素名称的实际意义或者代表什么 我们使用标签并不是他仅代表导航栏&#xff0c;只是将导航栏部分归为一个块。现实生活中&#xff0c;多使用之前都是使用div这个元素去构…...

一款非常不错的微信系统垃圾清理工具:微信清理大师,操作简单,清除较快。

微信清理大师 微信清理大师是一款专为微信所推出的系统垃圾清理工具。它的功能十分强大&#xff0c;可快捷清理微信内储存垃圾文件&#xff0c;操作十分简单&#xff0c;只需要轻轻一点&#xff0c;即可删除清理。 功能特点&#xff1a; 【一键清理】一键搞定无用垃圾&#x…...

PMP考前冲刺2.11 | 2023新征程,一举拿证

承载2023新一年的好运让我们迈向PMP终点一起冲刺&#xff01;一起拿证&#xff01;每日5道PMP习题助大家上岸PMP&#xff01;&#xff01;&#xff01;题目1-2&#xff1a;1.以下哪三种情况是Scrum主管应该注意的障碍&#xff1f;(选择三个)A. 阻碍敏捷能力的技术B. 缺乏团队授…...

yalc(比 yarn/npm link 更加友好的前端依赖库 link 方案)

参考链接: 【yalc 官方文档】:https://github.com/wclr/yalc 【yalc-watch 官方文档】:https://github.com/johot/yalc-watch 链接依赖库背景 以 D-SASS 前端框架为例,当我们在修改 D-SASS 前端框架时,当想着马上就要预览到修改的内容是否生效,于是在前端框架目录下进…...

Github | 个人资料自述文件配置的不完全总结

本文简单总结配置 Github 主页上个人资料自述文件的流程和参考文件。 更新&#xff1a;2022 / 02 / 11 Github | 配置个人主页的信息总览方法的不完全总结创建、删除个人资料自述文件编辑个人资料自述文件参考链接创建、删除个人资料自述文件 首推自然是官方说明文档 1&#…...

2022年12月电子学会Python等级考试试卷(二级)答案解析

目录 一、单选题(共25题&#xff0c;共50分) 二、判断题(共10题&#xff0c;共20分) 三、编程题(共2题&#xff0c;共30分) 青少年软件编程&#xff08;Python&#xff09;等级考试试卷&#xff08;二级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 运行下列程序…...

SpringCloud第二讲 Ribbon负载均衡源码分析

前言介绍&#xff1a; 这一讲我们将依据Eureka的负载均衡规则&#xff0c;Eureka的具体服务搭建以及服务注册和服务发现可以参考基于Eureka实现服务注册和服务发现_热爱Java的编程小白的博客-CSDN博客 Eureka的服务搭建之后便可以在这上面进行服务注册&#xff0c;如果存在两个…...

Clip-path实现按钮流动边框动画

前言 &#x1f44f;Clip-path实现按钮流动边框动画&#xff0c;速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现步骤 添加div标签 <div>苏苏_icon</div>添加样式 div {position: relative;width: 220px;height: 6…...

不停服更新应用的方案:蓝绿发布、滚动发布、灰度发布

原文网址&#xff1a;不停服更新应用的方案&#xff1a;蓝绿发布、滚动发布、灰度发布_IT利刃出鞘的博客-CSDN博客 简介 本文介绍不停服更新应用的方案&#xff1a;蓝绿发布、滚动发布、灰度发布。 升级服务器的应用时&#xff0c;要停止掉老版本服务&#xff0c;将程序上传…...

有趣的KaTeX(附源码)

两年半未见&#xff0c;甚是想念 给大家带来有趣的KaTeX\KaTeXKATE​X&#xff0c;可以放在洛谷主页 文章目录1234561 1#include<bits/stdc.h>\texttt{1 \color{orange}\#include <bits/stdc.h>}1 #include <bits/stdc.h> 2usingnamespacestd;\texttt{2 \col…...

Golang map笔记

map定义三种方式package mainimport "fmt"func main() {// map 的基本定义// 第一种方式 使用make分配数据空间var map1 map[string]stringmap1 make(map[string]string, 3)map1["no1"] "北京"map1["no2"] "天津"map1[&q…...

Android Jetpack组件之WorkManager高级概念介绍与使用(三)

一、介绍 通过前面两篇&#xff0c;我们基本掌握了组件的workmanager的接入&#xff0c;以及api的使用等。但是一个框架如果运用在复杂的项目中&#xff0c;肯定需要有其他额外的支持&#xff0c;介绍来我们将会介绍高级概念&#xff0c;以及对前面的知识点进行回顾与拓展。 高…...

Spring框架中用到的设计模式(都用到了哪些设计模式?)

文章目录简单工厂模式&#xff1a;工厂模式&#xff1a;单例模式&#xff1a;原型模式&#xff1a;迭代器模式&#xff1a;代理模式&#xff1a;适配器模式&#xff1a;观察者模式&#xff1a;模板模式&#xff1a;责任链模式&#xff1a;这是一道相对有难度的题目&#xff0c;…...

Python中的类和对象(4)

1. 构造函数 &#xff08;_ init _(self[,……])&#xff09; 在类中定义 _init _() 方法&#xff0c;可以实现在实例化对象的时候进行个性化定制&#xff1a; >>> class C: ... def __init__(self, x, y): ... self.x x ... self.y y ... …...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

高防服务器价格高原因分析

高防服务器的价格较高&#xff0c;主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因&#xff1a; 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器&#xff0c;因此…...

2.3 物理层设备

在这个视频中&#xff0c;我们要学习工作在物理层的两种网络设备&#xff0c;分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间&#xff0c;需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质&#xff0c;假设A节点要给…...

游戏开发中常见的战斗数值英文缩写对照表

游戏开发中常见的战斗数值英文缩写对照表 基础属性&#xff08;Basic Attributes&#xff09; 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...