目前大部分主流编译器的最新版本均支持了 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
给自己做的笔记
来源: http://www.bubuko.com/infodetail-2596701.html