介绍C++11标准的变长参数模板
目前大部分主流编译器的最新版本均支持了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-list,base-specifier-list,template-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标准的变长参数模板的更多相关文章
- C++11变长参数模板
[C++11变长参数模板] C++03只有固定模板参数.C++11 加入新的表示法,允许任意个数.任意类别的模板参数,不必在定义时将参数的个数固定. 实参的个数也可以是 0,所以 tuple<& ...
- C++中的变长参数
新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...
- c++11变长参数函数模板
By francis_hao Mar 25,2018 一个最简单的实例大概是这个样子: #include <iostream>using namespace std; /*变长参 ...
- 《OOC》笔记(3)——C语言变长参数va_list的用法
<OOC>笔记(3)——C语言变长参数va_list的用法 C语言中赫赫有名的printf函数,能够接受的参数数目不固定,这就是变长参数.C#里也有params这个关键字用来实现变长参数. ...
- (一)预定义宏、__func__、_Pragma、变长参数宏定义以及__VA_ARGS__
作为第一篇,首先要说一下C++11与C99的兼容性. C++11将 对以下这些C99特性的支持 都纳入新标准中: 1) C99中的预定义宏 2) __func__预定义标识符 3) _Pragma操作 ...
- Java语法糖初探(三)--变长参数
变长参数概念 在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用.形如 function(T …args).但是需要明 ...
- c(++)变长参数之整形(非字符串类型类似)
0.序言 变长参数,接触的第一个可变长参数函数是 printf , 然后是 scanf .他们的原型如下: printf: _Check_return_opt_ _CRT_STDIO_INLI ...
- Scala 变长参数
如果Scala定义变长参数 def sum(i Int*), 那么调用sum时,可以直接输入sum(1,2,3,4,5) 但是不可以sum(1 to 5) 必须要将1 to 5 强制为seq sum( ...
- 【Unix环境高级编程】编写变长参数函数
文件的格式输入输出函数都支持变长参数.定义时,变长参数列表通过省略号'...'表示, 因此函数定义格式为: type 函数名(parm1, parm2,parmN,...); Unix的变长参数通过v ...
随机推荐
- mongodb安装教程
MongoDB 下载及安装 MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 预编译二进制包下载地址:https://www. ...
- python 微信撤回消息
import itchatfrom itchat.content import *import osimport reimport time# 文件临时存储页rec_tmp_dir = os.path ...
- Unity导入模型出现 (Avatar Rig Configuration mis-match. Bone length in configuration does not match position in animation)?
昨天遇到这两个模型导入的问题,查了一下资料,自己摸索了一下解决方法..总结一下~ 出现的原因:(问题1)Warning 当模型文件导入以后并且设置Animation Type是Generic的时候,动 ...
- 504. Base 7
Given an integer, return its base 7 string representation. Example 1: Input: 100 Output: "202&q ...
- cloudflare 加https、加SSL(加CF处理)实操流程
建站过程中,少不了SSL证书等cf添加操作,cf,即cloudflare的简写 首先,点击如图“Add site”,弹出输入框后,填写已在如阿里云.goDaddy.freedom等域名平台购买的域名: ...
- “Linux内核分析”实验一报告
张文俊 + 原创作品转载请注明出处 + <Linux 内核分析> MOOC 课程 实验要求: 1.总结部分要求阐明自己对“计算机是如何工作的”理解: 2.博客中需要使用实验截图: 实验内容 ...
- 第二阶段冲刺——seven
个人任务: 马佳慧:设计界面背景,统一风格. 王金萱:整体运行测试上传到公网上的程序. 季方:修改优化已上传的代码. 司宇航:整体调试程序继续优化. 站立会议: 任务看板和燃尽图:
- 我是一个程序猿 ——《不是书评 :<我是一只IT小小鸟>》有感
读了刘未鹏先生的文章<不是书评 :<我是一只IT小小鸟>>,产生了诸多共鸣,更明白了不少道理. 首先是一个很平常的现象,进度条效应,在操作移动终端上的软件时,如果没有进度条,人 ...
- kickstart自动化安装
preboot execute environment预启动执行环境--intel开发的技术,计算机可以通过pxe协议从网络引导启动. 工作模式为客户端/服务器端的c/s模式 客户端从远端服务器下载镜 ...
- Alpha 冲刺五
团队成员 051601135 岳冠宇 051604103 陈思孝 031602629 刘意晗 031602248 郑智文 031602234 王淇 会议照片 项目燃尽图 项目进展 暂无实质性进展. 项 ...