目前大部分主流编译器的最新版本均支持了C++11标准(官方名为ISO/IEC14882:2011)大部分的语法特性,其中比较难理解的新语法特性可能要属变长参数模板(variadic template)了。下面先介绍一下这个语法特性在C++11标准中的描述。

14.5.3 变长参数模板(Variadic templates)

1、一个模板形参包template parameter pack)是一个接受零个或多个模板实参的模板形参。【例:

template<class ... Types> struct Tuple { };

Tuple<> t0;    // Types不含任何实参
Tuple<int> t1; // Types含有一个实参:int
Tuple<int, float> t2; // Types含有两个实参:int和float
Tuple<0> error; // 错误:0不是一个类型

  

——例结束】

2、一个函数形参包function parameter pack)是一个接受零个或多个函数实参的函数形参。【例:

template<class ... Types> void f(Types... args);

f();    // OK:args不含有任何实参
f(1); // OK:args含有一个实参:int
f(2, 1.0); // OK:args含有两个实参int和double

  ——例结束】

3、一个形参包要么是一个模板形参包,要么是一个函数形参包。

4、一个包扩展expansion)由一个模式pattern)和一个省略号组成。包扩展的实例中一个列表中产生零个或多个模式的实例。模式的形式依赖于扩展所发生的上下文中。【译者注:

template <typename... TS>   // typename... TS为模板形参包,TS为模式
static void MyPrint(const char* s, TS... args) // TS... args为函数形参包,args为模式
{
printf(s, args...);
}

  

包扩展会在以下上下文中发生:

——在一个函数形参包中(8.3.5);该模式是一个没有省略号的parameter-declaration。【译者注:

template <typename... Types>
void func(Types... args); // args为模式

  

——在一个模板形参包中,该包是一个包扩展(14.1):

——如果模板形参包是一个parameter-declaration;且该模式是没有省略号的parameter-declaration。【译者注:

template <typename... Types>    // Types为模式
void func(Types... args);

  

——如果模板形参包是具有一个template-parameter-list的一个type-parameter;且该模式是相应的type-parameter且没有省略号。【译者注:

// 这里模板形参包的模式为Classes
template <template <typename P, typename Q> class ... Classes>
struct MyAStruct;

  

——在一个初始化器列表中(8.5);模式是一个initializer-clause

——在一个base-specifier-list(条款10)中;模式是一个base-specifier

——在一个mem-initializer-list(12.6.2)中;模式是一个mem-initializer。

——在一个template-argument-list(14.3)中,模式是一个template-argument

——在一个dynamic-exception-specification(15.4)中;模式是type-id

——在一个attribute-list中(7.6.1);模式是一个attribute

——在一个alignment-specifier(7.6.2)中;模式是没有省略号的alignment-specifier

——在一个capture-list(5.1.2)中,模式是一个capture。

——在一个sizeof...表达式(5.3.3)中,模式是一个identifier

【例:

template<class ... Types> void f(Types ... rest);
template<class ... Types> void g(Types ... rest) {
f(&rest ...); // “&rest ...”是一个包扩展;“&rest”是其模式
}

  

——例结束】

5、一个形参包,其名字出现在一个包扩展的模式之内,被其包扩展而扩展。一个形参包的名字的一次出现仅仅被最内部所封闭的包扩展而扩展。一个包扩展模式应该命名一个或多个形参包,一个嵌套的包扩展不会扩展它们;这样的形参被称为模式中不被扩展的形参包。所有被一个包扩展所扩展的形参包应该具有相同数量的所指定的实参。没有被扩展的一个形参包的一个名字的一次出现是不良形式的。【例:

template<typename...> struct Tuple { };
template<typename T1, typename T2> struct Pair { }; template<class ... Args1> struct zip {
template<class ... Args2> struct with {
typedef Tuple<Pair<Args1, Args2> ... > type;
}; // 译者注:这里是对Pair<Args1, Args2>进行扩展
}; // T1是Tuple<Pair<short, unsignd short>, Pair<int, unsigned> >
typedef zip<short, int>::with<unsigned short, unsigned>::type T1; // 错误:对Args1和Args2指定了不同个数的实参
typedef zip<short>::with<unsigned short, unsigned>::type t2; template <typename ... Args>
void f(Args... args)
{ } template<class ... Args>
void g(Args ... args) { // OK:Args被函数形参包args扩展
f(const_cast<const Args*>(&args)...); // OK:“Args”与“args”被扩展
f(5 ...); // 错误:模式没包含任何形参包
f(args); // 错误:形参包“args”没被扩展
f(h(args ...) + args ...); // OK:第一个“args”在h内被扩展,第二个“args”在f内被扩展
}

  

——例结束】

6、一个包扩展的实例,它不是一个sizeof...表达式,产生一个列表E1,E2,E3,...,EN,这里,N是包扩展形参中元素的个数。每个Ei通过实例化该模式并用其第i个元素来代替每个包扩展形参来生成。所有Ei变为封闭列表中的元素。【注:列表的多样性会根据上下文而有所不同:expression-listbase-specifier-listtemplate-argument-list,等等。——注结束】当N为零时,扩展的实例产生一个空列表。这样的一个实例并不改变封闭构造的语法上的解释,甚至在忽略整个列表会导致不良形式的情况下或会在语法上产生奇异性的情况下。【例:

template<class... T>
struct X : T...
{
// 译者添加
X(T... args) { }
}; template<class... T> void f(T... values) {
X<T...> x(values...);
} template void f<>(); // OK:X<>没有基类;x是类型X<>被值初始化的一个变量 // 译者添加:
int main() {
struct Y { };
struct Z { };
f<>(); // 使用template void f<>();其中使用X<> x(); // 使用template<class... T> void f(T... values);
// 其内部使用X<Y, Z> x(Y(), Z());
// 而X<Y, Z>的定义为:struct X : Y, Z { X(Y arg1, Z arg2) { } };
f(Y(), Z());
}

  

——例结束】

7、一个sizeof...表达式的实例(5.3.3)产生了包含在它所扩展的形参包中元素个数的一个整数常量。

上述就是C++11标准对变长模板形参的描述。下面我将给出一些代码示例来做进一步的描述帮助大家更好地去理解,尤其是包扩展机制。// CPPTemplateTest.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

//============================================================================
// Name : CPPTest.cpp
// Author : Zenny Chen
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================ #include <iostream>
#include <typeinfo>
using namespace std;
#include <stdio.h>
#include <stdarg.h> struct MyTest; // 普通的C函数变长形参
static void MyCPrint(const char *s, ...)
{
char strBuffer[1024];
va_list ap;
va_start(ap, s);
vsprintf_s(strBuffer, s, ap);
va_end(ap);
printf(strBuffer);
} template <typename... TS> // typename... TS为模板形参包,TS为模式
static int MyPrint(const char* s, TS... args) // TS... args为函数形参包,args为模式
{
return printf(s, args...);
} template <typename... TS> // 模板形参包(template parameter pack)
static void DummyIter(TS... args) // 函数形参包(function parameter pack)
{
} template <typename T>
static T Show(T t, int n)
{
cout << "The value is: " << t << ", and n = " << n << endl;
return t;
} template <typename... TS>
static void Func(TS... args)
{
// 这里,Show(args, sizeof...(args))为模式,因此Show(args, sizeof...(args))...被扩展
// 每个args实例的类型为其所对应TS模板实参的类型
// 这里,Show(T, int)函数必须返回T类型,不能是void,由于void与TS...类型无法匹配
DummyIter(Show(args, sizeof...(args))...);
} // 请大家注意一下以下两种函数调用方式的不同!
template <typename... Types>
static void Foo(Types... args)
{
// 对DummyIter调用扩展MyPrint("The type is: %s\n", typeid(args).name())
DummyIter(MyPrint("The type is: %s\n", typeid(args).name()) ...);
puts("============");
// 对MyPrint调用扩展args
DummyIter(MyPrint("The first value is: %d, second is: %s, third is: %f\n", args...));
} // 对C++11标准14.5.3条款中的第5项中例子的进一步描述
template <typename... Types>
struct VariadicStruct : Types...
{ }; template <typename... Types>
static void ConstructStruct(void)
{
VariadicStruct<Types...>();
} template void ConstructStruct<>(void); // OK:VariadicStruct<>没有基类 template <typename... Types>
static void f(Types... args)
{
printf("The sample values are: %f, %f\n", args...);
} // 特化不带任何参数的f
template<> void f<>()
{
cout << "No arguments!" << endl;
} template <typename T1, typename T2>
static auto h(T1 t1, T2 t2) -> decltype(t1 * t2)
{
return t1 * t2;
} template <typename... Types>
static void g(Types... args)
{
// 这里,调用main函数中的g(10, 0.1)之后,会被展开为:
// f(h(10, 0.1) + 10, h(10, 0.1) + 0.1);
// 这里有两层包展开,首先对于f(),其模式为h(args...) + args
// 然后对于h(),其模式为args
// 因此,最右边的省略号其实是对整个(h(args...) + args)进行扩展
// 其等价于:f((h(args...) + args) ...);
f(h(args...) + args ...);
} extern "C" void cppTest(void)
{
MyCPrint("This is C print: %d, %s\n", 1, "Hello, world!");
MyPrint("This is my print: %d, %s\n", -1, "Hello, world!"); Func(-100); puts(""); Foo(3, "Hello", 0.25, "123"); // 对C++11标准14.5.3条款中的第5项中例子的进一步描述
puts("\n");
struct A {};
struct B {};
ConstructStruct<A, B>(); // 在此函数内部构造了VariadicStruct<A, B>
ConstructStruct<>(); // 在此函数内构造了VariadicStruct<>,它没有基类 g(10, 0.1);
g<>();
}
int main()
{
cppTest();
return 0;
}

  

  详细可以参考:https://www.cnblogs.com/zenny-chen/archive/2013/02/03/2890917.html

给自己做的笔记

介绍C++11标准的变长参数模板的更多相关文章

  1. C++11变长参数模板

    [C++11变长参数模板] C++03只有固定模板参数.C++11 加入新的表示法,允许任意个数.任意类别的模板参数,不必在定义时将参数的个数固定. 实参的个数也可以是 0,所以 tuple<& ...

  2. C++中的变长参数

    新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...

  3. c++11变长参数函数模板

    By francis_hao    Mar 25,2018   一个最简单的实例大概是这个样子: #include <iostream>using namespace std; /*变长参 ...

  4. 《OOC》笔记(3)——C语言变长参数va_list的用法

    <OOC>笔记(3)——C语言变长参数va_list的用法 C语言中赫赫有名的printf函数,能够接受的参数数目不固定,这就是变长参数.C#里也有params这个关键字用来实现变长参数. ...

  5. (一)预定义宏、__func__、_Pragma、变长参数宏定义以及__VA_ARGS__

    作为第一篇,首先要说一下C++11与C99的兼容性. C++11将 对以下这些C99特性的支持 都纳入新标准中: 1) C99中的预定义宏 2) __func__预定义标识符 3) _Pragma操作 ...

  6. Java语法糖初探(三)--变长参数

    变长参数概念 在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用.形如 function(T …args).但是需要明 ...

  7. c(++)变长参数之整形(非字符串类型类似)

    0.序言 变长参数,接触的第一个可变长参数函数是 printf   , 然后是 scanf   .他们的原型如下: printf: _Check_return_opt_ _CRT_STDIO_INLI ...

  8. Scala 变长参数

    如果Scala定义变长参数 def sum(i Int*), 那么调用sum时,可以直接输入sum(1,2,3,4,5) 但是不可以sum(1 to 5) 必须要将1 to 5 强制为seq sum( ...

  9. 【Unix环境高级编程】编写变长参数函数

    文件的格式输入输出函数都支持变长参数.定义时,变长参数列表通过省略号'...'表示, 因此函数定义格式为: type 函数名(parm1, parm2,parmN,...); Unix的变长参数通过v ...

随机推荐

  1. 您需要来自XXX的权限才能对此文件夹进行更改

    解决办法: cmd命令:del/f/s/q 文件夹

  2. nova状态同步

    服务初始化阶段 nova-compute服务启动时调用manager中的host初始化函数 self.manager.init_host() 在host初始化函数中完成如下操作: #初始化libvir ...

  3. 架构师修炼 II - 表达思维与驾驭方法论

    开篇之前我想先说说当年开发的那点事儿:大约10年前吧,我还是一个程序员的时候经常都是遇到这样的项目开发流程: 解决方案 :满足客户目的和投标用的一堆文档(不少还是互联网上抄的) ,是以Word为主的纯 ...

  4. 爱普生L313彩色打印相片

    操作环境: windows 和MAC 一.普通打印(默认选项) 1.爱普生L313 普通默认打印为快速不清晰打印. 2.以上打印效果出来图片比较快速出图,但是清晰度不够 二.照片打印设置 1.照片设置 ...

  5. 网站遭受大量CC攻击后的应对策略

    上周开始我网站遭受了一大波CC攻击,到目前为止仍在继续,作为一个建站小白,我感觉压力好大,又有新的问题要挑战了! 服务器架设在腾讯云,CC攻击很凶猛,带宽瞬间占满,于是在腾讯云后台配置安全组关闭了80 ...

  6. FSM Code Generator

    FSM Code Generator is a script code generator for Finite State Machine, it has a viaual designer bas ...

  7. vue入门全局配置

    全局配置 Vue.config 是一个对象,包含 Vue 的全局配置.可以在启动应用之前修改下列属性: silent 类型:boolean 默认值:false 用法: Vue.config.silen ...

  8. Scrum Meeting day 1

    第一次会议,在这一次的会议中,明确了任务目标,并将任务进行合理分配,并且规划了整个任务的初步计划. No_00:分工情况 姓名 分工   崔强      PM 杜正远 主力工程师 王嘉豪 主力工程师 ...

  9. 《Linux内核分析》 第四周

    [李行之 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] <Linux内 ...

  10. Alpha冲刺——day3

    Alpha冲刺--day3 作业链接 Alpha冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602634 ...