可变参数模板

【导读】: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,一经查实,立即删除!

相关文章

职业生涯起步不要去顶级公司

很多人都希望自己能够去世界顶级的公司工作&#xff0c;比如像宝洁、IBM、沃尔玛、微软等等这些可以在全球范围内 形成垄断的巨无霸&#xff0c;按常规来说就是财富500强排名在前100名的公司&#xff0c;这些公司实力台&#xff0c;业务增长都非常惊人&#xff0c;所以很多求职…

BAT批处理中的字符串处理

1、截取字符串 截取字符串可以说是字符串处理功能中最常用的一个子功能了&#xff0c;能够实现截取字符串中的特定位置的一个或多个字符。举例说明其基本功能&#xff1a; 复制代码 代码如下: echo off   set ifoabcdefghijklmnopqrstuvwxyz0123456789   echo 原字符串&…

【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…

ThreadPoolExecutor源码学习(2)-- 在thrift中的应用

thrift作为一个从底到上除去业务逻辑代码&#xff0c;可以生成多种语言客户端以及服务器代码&#xff0c;涵盖了网络&#xff0c;IO&#xff0c;进程&#xff0c;线程管理的框架&#xff0c;着实庞大&#xff0c;不过它层次清晰&#xff0c;4层每层解决不同的问题&#xff0c;可…

ANTLR VS FLEXBISON

1. ANTLR可以一站式的解决词法与语法解析器的生成。 FLEX&BISON需要配合使用&#xff0c;一个实现词法解析器&#xff0c;一个实现语法解析器。 2. ANTLR通过在文法文件中的设置&#xff0c;可以生成多个语言代码。 options {languageCpp;} options {languageCSharp;} op…

[POJ 1742] Coins 【DP】

题目链接&#xff1a;POJ - 1742 题目大意 现有 n 种不同的硬币&#xff0c;每种的面值为 Vi &#xff0c;数量为 Ni &#xff0c;问使用这些硬币共能凑出 [1,m] 范围内的多少种面值。 题目分析 使用一种 O(nm) 的 DP &#xff08;据说这是类多重背包&#xff1f;&#xff09;&…

ubuntu 18.04 显卡驱动

1. 禁用ubuntu默认显卡驱动&#xff0c;在/etc/modprobe.d/目录下创建blacklist-nouveau.conf文件并添加以下内容 blacklist vga16fb blacklist nouveau blacklist rivafb blacklist nvidiafb blacklist rivatu 2. 刷新内核&#xff0c;重启 update-initramfs -u reboo…

UE4 查看打包文件内容

UnrealPak.exe pak文件 -list -cryptokeys工程下Crypto.json文件 >输出文件

【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…

Target “xxx” links to target “Boost::filesystem“ but the target was not found

Boost::system Boost::thread Boost::timer等等替换为${Boost_LIBRARIES}即可

eclipse 断点调试快捷键

&#xff08;1&#xff09;CtrlM &#xff0d;&#xff0d;切换窗口的大小&#xff08;2&#xff09;CtrlQ &#xff0d;&#xff0d;跳到最后一次的编辑处&#xff08;3&#xff09;F2 &#xff0d;&#xff0d;当鼠标放在一个标记处出现Tooltip时候按F2则把鼠标移开时…

cygwin和mingw的区别

1、使用区别&#xff1a; cygwin/gcc和MinGW都是gcc在windows下的编译环境&#xff0c;但是它们有什么区别&#xff0c;在实际工作中如何选择这两种编译器。 cygwin/gcc完全可以和在linux下的gcc化做等号&#xff0c;这个可以从boost库的划分中可以看出来端倪&#xff0c;cygw…

轻量级web富文本框——wangEditor使用手册(4)——配置下拉菜单 demo

最新版wangEditor&#xff1a; 配置说明&#xff1a;http://www.wangeditor.com/doc.html demo演示&#xff1a;http://www.wangeditor.com/wangEditor/demo.html 下载地址&#xff1a;https://github.com/wangfupeng1988/wangEditor ----------------------------------------…

Linux下解决发布Qt程序报错:it could not find or load the Qt platform plugin “xcb” in “”

简述 用Qt5.8版本在ubuntu16.04版本下编写Qt应用程序&#xff0c;生成release版本并打包&#xff0c;到另一台无Qt环境的linux系统中运行。 网上通常是按以下几个步骤进行&#xff1a; 1、生成release程序。 2、拷贝release程序到一个新文件夹&#xff0c;执行一个脚本文件。脚…

ViewGroup之getScrollX()

举个例子&#xff0c;一个横向的ViewGroup&#xff0c;如果每个View的宽度为300&#xff0c;那么当前显示第一个View的时候getScrollX()返回值为0&#xff0c;当你 向左移动第一个View&#xff0c;移动距离为30&#xff0c;那么此时getScrollX()的返回值为30&#xff0c;如果当…

Linux环境中Qt程序的手工发布

Qt 5.7.0 编写的程序需要部署到一台没有安装Qt的目标机器上&#xff0c;程序采用C与QML混合编程&#xff0c;QML做界面&#xff0c;C写逻辑。 环境说明 开发环境 Ubuntu 16.04.1 LTS 运行环境 CentOS 7.2.1511 下面描述这个手工操作的发布过程。 1、在开发环境中采用Re…

VS2017编译UE4.19.2报错

error C4577: 在未指定异常处理模式的情况下使用了 "noexcept"&#xff1b;不一定会在异常时终止。指定 /EHsc 修改 VCToolChain.cs&#xff0c; 在 AppendCLArguments_Global 函数中增加 Arguments.Add("/EHsc");