C++ -- variadic template (可变参数模板)

2023-10-26

C++ -- variadic template (可变参数模板)

作者:唐风

主页:http://www.cnblogs.com/muxue


C++11 语言核心的改进中,最为关注的有 rvalue reference (这里有一篇拙作),lambda,variadic template。rvalue 规则稍微复杂,但一旦理解和记住了,应用上就没有什么困难。lambda 其实是一个“很自然”的语言设施,除了语法稍显诡异之外,习惯了就能马上用上,而且是能广泛用上的好东西。variadic template 这个新特性不像前两者,它本身的语法规则并不复杂,但是应用的时候确比较费脑子,好在这个技术主要是用在库的实现中,一个实现得好的库,并不会让我们对实现的细节作任何的要求

variadic template 特性本身是一个很自然的需求,它完善了 C++ 的模板设计手段。原来的模板参数可以使类和函数的参数类型“任意化”,如果再加上“参数个数的任意化”,那么在参数方面的设计手段就基本上齐备了,有了variadic template 显然可以让设计出来的函数或是类有更大的复用性。因为有很多处理都是与“处理对象的个数”关系不大的,比如说打屏(printf),比如说比较大小(max,min),比如函数绑定子(bind,function要对应各种可能的函数就要能“任意”参数个数和类型)。如果不能对应任意个参数,那么就总会有人无法重用已有的实现,而不得不再重复地写一个自己需要的处理,而共通库的实现者为了尽可能地让自己写的类(函数)能复用在更多的场景,也不得不重复地写很多的代码或是用诡异的技巧,宏之类的去实现有限个“任意参数”的对应。(像TR1中的 bind 等)。

一、基本语法

声明一个带有可变参数个数的模板的语法如下所示:

  1. template<typename ...Elementclass tuple;
  2.  
  3. tuple<intstringa;  // use it like this

在模板参数 Element 左边出现省略号 ... ,就是表示 Element 是一个模板参数包(template type parameter pack)。parameter pack(参数包)是新引入 C++ 中的概念,比如在这个例子中,Element 表示是一连串任意的参数打成的一个包。比如第2行中,Element 就是 int, string这个参数的合集。不仅“类型”的模板参数(也就是typename定义的参数)可以这样做,非类型的模板参数也可以这样做。比如下面这个例子:

  1. template<typename Tunsigned PrimaryDimesionunsigned...Dimesions>
  2. class array { /**/ };
  3.  
  4. array<double, 3, 3> rotation_matrix//3x3 ratiation matrix

现在我们知道parameter pack了,怎么在程序中真正具体地去处理打包进来的“任意个数”的参数呢?我原来以为,编译器会提供一些像get_param<1>(Element) 之类的内建的“参数抽取函数”给程序员使用结果不是!!看来我的思路还是太“过程式了”。其实 C++11 用的是 unpack 和类似函数重载似的“模板特化”来抽取参数的。这是应用 variadic tempate 最“坑爹”的部分,因为它要求对“递归”和“人肉代码展开”有一定的功力啊。还是看例子吧:

  1. template<typename... Elements> class tuple;
  2. template<typename Head, typename... Tail>
  3. class tuple<Head, Tail...> : private tuple<Tail...> {
  4.     Head head;
  5. public:
  6.     /* implementation */
  7. };
  8. template<>
  9. class tuple<> {
  10.     /* zero-tuple implementation */
  11. };

第1行声明了一个可以对应任意参数的tuple类,第2行到7行声明了这个类的一个部分特化,注意,这就是抽取参数的典型方法了。给个图还是最方便用来理解的:

image

(如果看不全,点击它可以看全图)

好吧,我觉得这个图是本篇博客中最有价值的部分了,转载一定要注明出处啊!

只说明一下针对 parameter pack 相对的另一个概念,模板参数后面带省略号 ... 就是一个解包(unpack),会把这个参数所表示的参数列表解开后去匹配新的模板,或是进行模板展开。其它的,我觉得有上面这个图,实在不需要再多费什么笔墨去讲这一部分了。

二、例子

新的标准库里,有很多个库都直接依赖于 variadic template 这个语言特性,比如,tuple,bind,function。但他们比较复杂,也不够“惊艳”。C++ 老爸的 C++11 的 FQA 和 Wikipedia 的例子都是“类型安全”的printf:

  1. void printf(const char *s)
  2. {
  3.     while (*s) {
  4.         if (*s == '%') {
  5.             if (*(s + 1) == '%') {
  6.                 ++s;
  7.             }
  8.             else {
  9.                 throw std::runtime_error("invalid format string: missing arguments");
  10.             }
  11.         }
  12.         std::cout << *s++;
  13.     }
  14. }
  15.  
  16. template<typename T, typename... Args>
  17. void printf(const char *s, T value, Args... args)
  18. {
  19.     while (*s) {
  20.         if (*s == '%') {
  21.             if (*(s + 1) == '%') {
  22.                 ++s;
  23.             }
  24.             else {
  25.                 std::cout << value;
  26.                 // call even when *s == 0 to detect extra arguments
  27.                 printf(s + 1, args...);
  28.                 return;
  29.             }
  30.         }
  31.         std::cout << *s++;
  32.     }
  33.     throw std::logic_error("extra arguments provided to printf");
  34. }

这个例子确实很棒,够短。仅仅X行的代码中,就用到了选择(if)和循环(while),递归(printf自身的递归),重载(cout的<<对种输出类型的重载),异常。这个例子不细讲,大家试着用“上面的图”来试试分析一下吧。(这个例子用来面试,让面试者来解析下是不是不错?……)

三、杂想

这里有篇文章有一些关于这个特性加入 C++ 的动机,简而言之,像 function ,bind 之类如果没有这个特性,实现起来会非常的麻烦,而且这样实现出来的代码也会使得编译时间变得很长。

image

你看,如果要支持39个参数,编译时间会到700秒。而使用 variadic template 之后,代码小了 40 K,同时各种参数个数的情况下编译时间都小于 1 秒。

这很好。但是,这个特性用起来真的不是那么容易,你也看到了。在和其它的特性一起使用的时候(多重继承,右值引用)等在一起的时候,会更加地“复杂”。但它是一个利器,用得好,会造出非常好用的库。否则,我们就用库好了,不要为去学习,记住和非要在日常编程中使用它而烦恼了。

四、参考资料

  1. Variadic Templates for GCC:http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html
  2. 维基百科:http://en.wikipedia.org/wiki/Variadic_template
  3. C++11当时的提案:N2080 (这个特别好,或是最好,我所见过的)
---- 总会有一个人需要你的分享~! 唐风: www.cnblogs.com/muxue ------
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ -- variadic template (可变参数模板) 的相关文章

  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • 在非活动联合成员上使用“std::addressof”是否定义明确[重复]

    这个问题在这里已经有答案了 下面的代码是尝试实现constexpr的版本offsetof在 C 11 中 它可以在 gcc 7 2 0 和 clang 5 0 0 中编译 这取决于申请std addressof工会非活跃成员的成员 这是明确
  • 元组在 VS2012 中如何工作?

    Visual Studio 2012 功能 tuples但不是可变参数模板 这是如何完成的 如何在不使用可变模板的情况下实现元组 简而言之 微软做了与之前在 NET 中实现类似元组的数据类型完全相同的事情 创建许多版本 每个版本都有固定数量
  • C++11 基于范围的 for 循环效率“const auto &i”与“auto i”

    在 C 11 中 我可以像这样迭代一些容器 for auto i vec std cout lt lt i lt lt std endl 但我知道这是不必要的 不必要地 因为我只需要print的价值观vec 复制 EDIT 的每个元素vec
  • 局部静态变量初始化是线程安全的[重复]

    这个问题在这里已经有答案了 假设我有一个包含三个静态函数的类 如下所示 include
  • clang C++11 调用

    虽然这里有一些关于 clang 的 C 11 支持的问题 但我似乎无法得到clang 吃掉我的 C 11 代码 clang version clang version 2 9 tags RELEASE 29 final Target x86
  • 可以安全使用 vector.emplace_back( new MyPointer );矢量内部的故障会导致内存泄漏吗?

    使用安全吗 vector emplace back new MyPointer 或者抛出异常或向量内部的某些故障是否会导致内存泄漏 最好执行以下某种形式 首先将指针放入临时 unique ptr 中 vector emplace back
  • 与函数指针转换相关的 lambda 对象的生命周期

    下列的这个答案 https stackoverflow com questions 4726768 returning functions in c 4730655 4730655我现在想知道 lambda 生命周期的规则是什么 以及它们与
  • SFINAE decltype 逗号运算符技巧

    读完马蒂厄的回答后here https stackoverflow com a 9531274 955273 我决定自己尝试一下 我的尝试无法编译 因为 SFINAE 没有启动并剔除has foo尝试访问的函数T foo error str
  • 为什么 C++ 元组如此奇怪?

    我通常创建自定义structs将不同类型的值分组在一起时 这通常很好 而且我个人发现命名成员访问更容易阅读 但我想创建一个更通用的 API 在其他语言中广泛使用元组后 我想返回类型的值std tuple但发现它们在 C 中使用比在其他语言中
  • 返回右值 - 这段代码有什么问题? [复制]

    这个问题在这里已经有答案了 我遇到了以下代码片段 std string test std string m Hello return std move m int main std string m test 我知道上面的代码是不正确且不安
  • C++ 中的垃圾收集——为什么?

    我不断听到人们抱怨 C 没有垃圾回收功能 我还听说 C 标准委员会正在考虑将其添加到该语言中 恐怕我只是不明白它的意义 使用 RAII 和智能指针消除了它的需要 对吗 我唯一的垃圾收集经验是在几台廉价的八十年代家用计算机上 这意味着系统会时
  • C++ 类的互斥成员导致编译错误

    我不确定为什么当我向 myClass 添加互斥体成员时会发生这种情况 在本例中为 mu Error C2661 std tuple lt void thiscall MyNameSpace myClass void MyNameSpace
  • C++11 中具有 C 链接的复杂类型

    我需要将 C 库的标头包含到我的 C 11 代码中 现在 标头提供了涉及大量的例程和数据结构double complex到处都是 例如 include
  • 如何删除可变参数模板的最后一个参数

    我有以下结构 我想从中删除最后一个参数index sequence template lt std size t values gt struct index sequence I need something like template
  • 如何将 Visual-Studio 2010 切换到 c++11

    我是 c 编程新手 我想尝试 c 11 新功能 那么我要问的是如何切换 Visual studio 2010 才能编译 c 11 源代码 你可以参考这个表 VC10 中的 C 0x 核心语言功能 表格 http blogs msdn com
  • 对象返回时是否保证被移动?

    我知道 当将对象按值传递给函数时 如果存在移动构造函数 则始终会调用移动构造函数 假设没有复制省略 按值返回对象怎么样 例如 假设我们有一堂课Foo它有一个移动构造函数 我们有一个返回一个的函数Foo object Foo g Foo f
  • 与运算符<<(操作数类型std::ostream)C++ OOP和Point不匹配[重复]

    这个问题在这里已经有答案了 我试图显示我通过成员函数创建的 Point 类的 p 对象 我已将 Point p 作为参数传递给我的程序的 void displayPoint Point p 成员函数 但我的程序中出现以下编译错误 D OOP
  • 使用 std::function w/ std::bind 时的 EXC_BAD_ACCESS

    升级到 XCode 5 后 使用 std function 和 std bind 似乎会生成 EXC BAD ACCESS 异常 看起来好像 std function 实现中的 base 指针最终为空 导致访问错误 但我不清楚为什么会出现这
  • 没有“对 *this”功能的右值引用的解决方法

    我有一个围绕可移动对象的代理容器类 并希望代理能够隐式生成对底层对象的右值引用 但仅当代理本身被移动时 我相信我将能够按照提案 n2439 实施此行为 将移动语义扩展到 this http www open std org jtc1 sc2

随机推荐

  • 关于51单片机串口中断的理解

    关于51单片机串口中断的理解 关于这个问题找了好几个帖子 都没看到能让我明白的 自己就想了想 又看了看 新手不一定说得对 您凑合着看看 要不对的话 望指正 首先 我们在SCON中设置的时候 一般都会将接收使能位REN置1 其次 串口的收发都
  • Visual Studio VS2022 设置编码为 utf-8

    1 扩展 gt 管理扩展 2 搜索utf8扩展 点击完成安装 重启VS2022就可以生效了
  • ios sdk 穿山甲_GitHub - ArthurKnight/flutter_ad_pangolin_plugin: iOS flutter 穿山甲插件

    Pangolin 前言 在使用本插件前请认真 仔细阅读穿山甲官方文档 本插件将尽量保留SDK内容和各API相关内容 如出现在官方文档以外报错信息可以留言issue 或通过文末联系方式联系作者 注明来意 针对你可能会遇到的问题 在使用过程中可
  • 爬网页不用写代码?什么操作

    实验环境 Python 3 9 12 配置文件格式 爬页面基本是先请求再解析然后再请求然后不断重复 页面结构相对固定的情况下 弄一种配置文件来描述爬取步骤 这样就不用写代码了 想要爬不同的页面只写配置不写代码 所以
  • 基于SM2证书实现SSL通信

    参考链接 基于openssl和国密算法生成CA 服务器和客户端证书 MY CUP OF TEA的博客 CSDN博客 基于上述链接 使用国密算法生成CA 服务器和客户端证书 并实现签名认证 openssl实现双向认证教程 服务端代码 客户端代
  • idea DataGrip 使用图解教程

    日常开发中少不了各种可视化数据库管理工具 如果需要同时能连接多种数据库 大家肯定都会想到 DBeaver Navicat Premium 本文介绍另一个十分好用且强大的工具 DataGrip DataGrip 是 JetBrains 公司推
  • 用串口控制kobuki, 绕过ROS系统

    介绍 下面所做的事情 用串口来控制kobuki底座运动 绕过ros系统 首先测试一下串口命令是否可用 硬件设备 kobuki turtlebot的底座 kobuki usb连接 用usb线将kobuki和电脑连接起来 不是25针的接口 wi
  • vscode配置setting.json文件实现eslint自动格式代码

    一 ESlint Vetur 实现ESlint代码规范 二 重点 旧版本 旧版本配置在setting json 会出现警告 eslint autoFixOnSave true eslint validate javascript langu
  • Python金融大数据分析——第8章 高性能的Pyhon 笔记

    第8章 高性能的Python 8 1 Python范型与性能 8 2 内存布局与性能 8 3 并行计算 8 3 1 蒙特卡洛算法 8 3 2 顺序化计算 8 4 多处理 8 5 动态编译 8 5 1 介绍性示例 8 5 2 二项式期权定价方
  • jenkins编译java项目时无法读取pom.xml文件

    解决方法 勾选build下的启用触发下游项目 构建时阻止下游触发器 Build Enable triggering of downstream projects Block downstream trigger when building
  • 基于Python的论文管理与提交系统-任务分配接收Python爬虫安装数据分析与可视化计算机毕业设计

    更多项目资源 最下方联系我们 目录 一 项目技术介绍 二 项目配套文档 部分内容 资料获取 一 项目技术介绍 该项目含有源码 文档 PPT 配套开发软件 软件安装教程 项目发布教程 包运行成功以及课程答疑与微信售后交流群 送查重系统不限次数
  • 已解决sc delete MongoDB卸载MongoDB拒绝访问。

    已解决sc delete MongoDB卸载MongoDB拒绝访问 文章目录 报错问题 报错翻译 报错原因 解决方法 千人全栈VIP答疑群联系博主帮忙解决报错 报错问题 粉丝群里面的一个小伙伴遇到问题跑来私信我 想卸载MongoDB数据库
  • Hexo博客搭建及配置

    Hexo 是高效的静态站点生成框架 基于 Node js 通过 Hexo 你可以轻松地使用 Markdown 编写文章 除了 Markdown 本身的语法之外 还可以使用 Hexo 提供的 tag 插件 来快速的插入特定形式的内容 Hexo
  • 数据库表的关系

    表与表之间一般存在三种关系 即一对一 一对多 多对多关系 下面分别就三种关系讲解数据库相关设计的思路和思考过程 1 一对一关系 例如 下面的一张表 保存了人的相关信息 有男有女 要求查处所有的夫妻 sql代码 CREATE TABLE IF
  • Maven install报错To see the full stack trace of the errors, re-run Maven with the -e switch.解决

    今天在使用maven进行springCloud的打包jar包时 一直报错 To see the full stack trace of the errors re run Maven with the e switch 首先 我是使用了本地
  • Python学习笔记 面向对象编程

    类和对象 定义类 Python支持面向对象编程 下面是一个例子 我们可以看到 在Python中声明类和其他语言差不多 不过实际上差别还是挺大的 首先 Python没有严格意义上的构造函数 只有一个 init self XXX 函数 该函数和
  • IC验证方法基础

    数字IC的设计流程 如下图所示 其中讲到形式验证的时候就懵了 当时老师说 其实我也记不太清了 就从网上找了一下 形式验证 Formal Verification 是一种IC设计的验证方法 它的主要思想是通过使用数学证明的方式来验证一个设计的
  • C语言刷题之摩尔投票法

    目录 1 引入 2 摩尔投票算法 3 基本步骤 摩尔投票法分为两个阶段 1 抵消阶段 2 检验阶段 4 代码实现 5 扩展沿伸 6 总结 1 引入 我们来看一个问题 假设有一个无序数组长度为n 要求找出其中出现次数超过n 2的数 要求时间复
  • 【算法日志】动态规划:动态规划简介及其简单应用(day33)

    算法随想录刷题60Day 目录 动态规划简介 动态规划简单应用 斐波那契 爬楼梯 使用最小开支爬楼 动态规划简介 动态规划 Dynamic Programming 是一种解决复杂问题的算法设计思想 它的主要思路是将原问题拆分若干个子问题 并
  • C++ -- variadic template (可变参数模板)

    C variadic template 可变参数模板 作者 唐风 主页 http www cnblogs com muxue C 11 语言核心的改进中 最为关注的有 rvalue reference 这里有一篇拙作 lambda vari