可变参数模板

【导读】:C++ 可变参数模板对参数进行了高度泛化,它能表示0到任意个数、任意类型的参数。相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以它也是C++11中最难理解和掌握的特性之一。那么请大家跟随小编,一起来学习吧。

 

以下是正文


 

 

概述

 

在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。

 

可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号“…”:

 

//T叫模板参数包,args叫函数参数包template<class ... T> void func(T ... args){//可变参数模板函数}func();    // OK:args不含有任何实参func(1);    // OK:args含有一个实参:intfunc(2, 1.0);   // OK:args含有两个实参int和double

T叫模板参数包,args叫函数参数包。

 

省略号“…”的作用有两个:

 

(1)声明一个参数包,这个参数包中可以包含0到任意个模板参数

 

(2)在模板定义的右边,可以将参数包展开成一个一个独立的参数

 

可变参数模板函数

 

可变参数模板函数的定义

 

一个可变参数模板函数的定义如下:

 

#include <iostream>using namespace std;template<class ... T> void func(T ... args){//可变参数模板函数    //sizeof...(sizeof后面有3个小点)计算变参个数    cout << "num = " << sizeof...(args) << endl;}int main(){    func();     // num = 0    func(1);    // num = 1    func(2, 1.0);   // num = 2    return 0;}

 

运行结果如下:
图片参数包的展开

 

递归方式展开

 

通过递归函数展开参数包,需要提供一个参数包展开的函数和一个递归终止函数。

 

#include <iostream>using namespace std;//递归终止函数void debug(){    cout << "empty\n";}//展开函数template <class T, class ... Args>void debug(T first, Args ... last){    cout << "parameter " << first << endl;    debug(last...);}int main(){    debug(1, 2, 3, 4);    return 0;}

 

运行结果如下:
图片

递归调用过程如下:

 

debug(1, 2, 3, 4);debug(2, 3, 4);debug(3, 4);debug(4);debug();

 

通过可变参数模板实现打印函数:

 

#include <iostream>#include <stdexcept>using namespace std;void Debug(const char* s){    while (*s)    {        if (*s == '%' && *++s != '%')        {            throw runtime_error("invalid format string: missing arguments");        }        cout << *s++;    }}template<typename T, typename... Args>void Debug(const char* s, T value, Args... args){    while (*s)    {        if (*s == '%' && *++s != '%')        {            cout << value;            return Debug(++s, args...);        }        cout << *s++;    }    throw runtime_error("extra arguments provided to Debug");}
int main(){    Debug("a = %d, b = %c, c = %s\n", 250, 'm', "mike");    return 0;}

 

运行结果如下:
图片

非递归方式展开

 

#include <iostream>using namespace std;template <class T>void print(T arg){    cout << arg << endl;}template <class ... Args>void expand(Args ... args){    int a[] = { (print(args), 0)... };}int main(){    expand(1, 2, 3, 4);    return 0;}

运行结果如下:
图片

expand函数的逗号表达式:(print(args), 0), 也是按照这个执行顺序,先执行print(args),再得到逗号表达式的结果0。

 

同时,通过初始化列表来初始化一个变长数组,{ (print(args), 0)… }将会展开成( (print(args1), 0), (print(args2), 0), (print(args3), 0), etc…), 最终会创建一个元素只都为0的数组int a[sizeof…(args)]。

 

可变参数模板类

 

继承方式展开参数包

 

可变参数模板类的展开一般需要定义2 ~ 3个类,包含类声明和特化的模板类:

​​​​​​​

#include <iostream>#include <typeinfo>using namespace std;template<typename... A> class BMW{};  // 变长模板的声明template<typename Head, typename... Tail>  // 递归的偏特化定义class BMW<Head, Tail...> : public BMW<Tail...>{//当实例化对象时,则会引起基类的递归构造public:    BMW()    {        printf("type: %s\n", typeid(Head).name());    }    Head head;};template<> class BMW<>{};  // 边界条件int main(){    BMW<int, char, float> car;    return 0;}

 

运行结果如下:
图片

模板递归和特化方式展开参数包

​​​​​​​

#include <iostream>using namespace std;template <long... nums> struct Multiply;// 变长模板的声明template <long first, long... last>struct Multiply<first, last...> // 变长模板类{    static const long val = first * Multiply<last...>::val;};template<>struct Multiply<> // 边界条件{    static const long val = 1;};int main(){    cout << Multiply<2, 3, 4, 5>::val << endl; // 120    return 0;}

 

运行结果如下:
图片

- EOF -

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432106.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】44. Wildcard Matching (2 solutions)

Wildcard Matching Implement wildcard pattern matching with support for ? and *. ? Matches any single character. * Matches any sequence of characters (including the empty sequence).The matching should cover the entire input string (not partial).The functi…

ANTLR 4(一)Getting Started

1. 配置java环境 安装jdk1.7以上版本&#xff0c;我装的是“jdk-8u181-windows-x64.exe”。 设置 “C:\Program Files\Java\jdk1.8.0_181\bin" 到path环境变量。 设置 "C:\Program Files\Java\jdk1.8.0_181" 到 JAVA_HOME 环境变量。 设置 "%JAVA_HOME%\l…

【iOS】Quartz2D图片剪切

一、使用Quartz2D完成图片剪切1.把图片显示在自定义的view中  先把图片绘制到view上。按照原始大小&#xff0c;把图片绘制到一个点上。  代码&#xff1a; 1 - (void)drawRect:(CGRect)rect 2 { 3 UIImage *image2[UIImage imageNamed:"me"]; 4 [image2…

InstallShield 2020

1.新建工程&#xff1a;HOME-New-Basic MSI 2.Gereral Information设置&#xff08;根据需求自行设置&#xff0c;例如下图&#xff09; 3.Files and Folders设置 a.将需打包工程拖拽至下方目标文件夹内 b.创建新的Feature c.设置结果如下 4.Shortcuts设置 a.Desktop右键-New…

iOS项目开发— CoreLocation的定位服务和地理编码与发编码实现

一、CoreLocation简介 1.在移动互联网时代&#xff0c;移动app能解决用户的很多生活琐事&#xff0c;比如 &#xff08;1&#xff09;导航&#xff1a;去任意陌生的地方 &#xff08;2&#xff09;周边&#xff1a;找餐馆、找酒店、找银行、找电影院 2.在上述应用中&#xff0c…

OpenGL-坐标系

笛卡尔坐标系 二维绘图&#xff1a;笛卡尔坐标有一个X轴和一个Y轴组成&#xff0c;X轴为水平方向&#xff0c;Y轴为垂直方向&#xff0c;X和Y相互垂直二维笛卡尔坐标系 三维绘图&#xff1a;笛卡尔坐标多了一个Z轴&#xff0c;Z轴同时垂直于X和Y轴。Z轴的实际意义代表着三维物…

UML类图与类间六种关系表示

1.类与类图 类封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性,操作,关系的对象集合的总称. 类图是使用频率最高的UML图之一. 类图用于描述系统中所包含的类以及它们之间的相互关系,帮助开发人员理解系统,它是系统分析和设计阶段的重要产物,也是系统编码和测试…

PlacementBrowser源码分析

PlacementBrowser的源码在 Editor/PlacementMode下面 一、Placement分类的创建 1. Placement分类被存贮在 FPlacementModeModule 的成员变量 Categories里面。 2. 在 FPlacementModeModule::StartupModule() 创建出所有的Placement分类 3. 每个大类由RegisterPlacementCat…

menu源码分析

1. 菜单根据功能不同被划分了不同的section&#xff0c;不同的section的源码被分布在不同的地方 例如&#xff0c;主菜单的源码在Editor/Mainframe&#xff0c;datavaildation菜单的源码在Plugins\Editor\DataValidation下 2. 以主菜单为例&#xff0c;菜单以如下方法构建 首先…

根据select不同的选项实现相应input框添加项的显示

实现效果&#xff1a; 1.单击包时&#xff0c;显示包时的添加项 2.单击包里程&#xff0c;显示包里程的添加项 二 代码实现&#xff1a; 给select添加change事件 获取当前select的value 根据value判断对象显示其添加项框 <script> $(document).ready(function() {$(#typ…

内联汇编用法

在 Linux 代码中&#xff0c;经常可以看到在 C 代码中&#xff0c;嵌入部分汇编代码&#xff0c;这些代码要么是与硬件体系相关的&#xff0c;要么是对性能有关键影响的。 在很久以前&#xff0c;我特别惧怕内嵌汇编代码&#xff0c;直到后来把汇编部分的短板补上之后&#xf…

epoll怎么实现的

epoll 可以说是编写高性能服务端程序必不可少的技术&#xff0c;在介绍 epoll 之前&#xff0c;我们先来了解一下 多路复用I/O 吧。 多路复用I/O 多路复用I/O&#xff1a;是指内核负责监听多个 I/O 流&#xff0c;当任何一个 I/O 流处于就绪状态&#xff08;可读或可写&#…

平衡树以及AVL树

平衡树是计算机科学中的一类数据结构。 平衡树是计算机科学中的一类改进的二叉查找树。一般的二叉查找树的查询复杂度是跟目标结点到树根的距离&#xff08;即深度&#xff09;有关&#xff0c;因此当结点的深度普遍较大时&#xff0c;查询的均摊复杂度会上升&#xff0c;为了更…

法线和法线贴图

法线和法线贴图 1、法线无处不在&#xff0c;这是图形学基础中的基础。 2、法线贴图&#xff0c;凹凸图&#xff0c;位移图等等&#xff0c;在图形学历史上有着比较重要的位置&#xff0c;在很多图形学的架构中都有应用&#xff0c;典型的例如延迟渲染架构。 法线 法线&…

Unicode、UTF-8、UTF-16

计算机起源于美国&#xff0c;上个世纪&#xff0c;他们对英语字符与二进制位之间的关系做了统一规定&#xff0c;并制定了一套字符编码规则&#xff0c;这套编码规则被称为ASCII编码 ASCII 编码一共定义了128个字符的编码规则&#xff0c;用七位二进制表示 ( 0x00 - 0x7F ), …

IE11 全新的F12开发者工具

我讨厌debug&#xff0c;相信也没多少开发者会喜欢。但是当代码出错之后肯定是要找出问题出在哪里的。不过网页开发的时候遇到 BUG 是一件再正常不过的事情了&#xff0c;我们不能保证自己的代码万无一失&#xff0c;于是使用浏览器的开发者工具调试是我们解决问题最快捷的方法…

OpenXLSX 中文字段读取问题

在读取excel的时候发现有些中文字段无法读取&#xff0c;通过把excel文件解压后对比发现&#xff0c;正常读取和不 能正常读取的中文字段在sharedString.xml中存储的格式有差异&#xff0c;取其中一个字段&#xff0c;如下图&#xff1a; 正常读取的 不能读取的 对比可以看到…

[翻译] ZLHistogramAudioPlot

ZLHistogramAudioPlot A hardware-accelerated audio visualization view using EZAudio, inspired by AudioCopy. ZLHistogramAudioPlot was originally developed for Murmur. 这是使用了EZAudio,一个硬件加速的audio可视化view,灵感来自于AudioCopy.ZLHistogramAudioPlot这个…