博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++11——可变参数模板
阅读量:4505 次
发布时间:2019-06-08

本文共 4632 字,大约阅读时间需要 15 分钟。

    在c++11之前,类模板和函数模板只能含有固定数量的模板参数,c++11增加了可变模板参数特性:允许模板定义中包含0到任意个模板参数。声明可变参数模板时,需要在typename或class后面加上省略号"..."。 

    省略号的作用有两个: 
1. 声明一个参数包,这个参数包中可以包含0到任意个模板参数 
2. 在模板定义的右边,可以将参数包展开成一个一个独立的参数

1. 可变参数模板函数

    可变参数模板函数的定义如下:

template
void f(T... args){ cout << sizeof...(args) << endl; //sizeof...(args) 取得可变参数的个数}f();f(1, 2);f(1, 2.3, "hello");

 

    参数包可以包含0个或者多个参数,如果需要用参数包中的参数,则一定要将参数包展开。有两种展开参数包的方法:(1)通过递归的模板函数来展开参数包;(2)通过逗号表达式和初始化列表方式展开参数包。

展开参数包 

(1)递归函数方式展开参数包 
    需要提供一个参数包展开的函数和一个递归终止函数,二者同名。递归终止函数的参数可以为0,1,2或者多个(一般用到0个或1个),当参数包中剩余的参数个数等于递归终止函数的参数个数时,就调用递归终止函数,则函数终止。

#include
using namespace std;//递归终止函数void print(){ cout << "empty" << endl;}//展开函数template
void print(T head, Args... rest){ cout << "parameter = " << head << endl; print(rest...);}int main(){ print(1,2,3,4); return 0;}//当调用print(1,2,3,4)时,先后调用print(2,3,4), print(3,4),print(4),最终调用print()终止。如果递归终止函数为template
void print(T a){ cout << a << endl;}则函数调用到 print(4)就终止。

 

还可以通过type_traits方式来定义同名但不同参数的函数,分别实现递归终止和展开函数,从而展开参数包

//相当于递归终止函数template
typename std::enable_if
::value>::type printtp(Tuple t){ }//相当于展开函数template
typename std::enable_if
< std::tuple_size
::value>::type printtp(Tuple t){ std::cout << std::get
(t) << std::endl; //打印出元组中的第i个 printtp
(t);}template
void print(Args... args){ printtp(std::make_tuple(args...);}

 

(2)初始化列表方式展开参数包 

    递归函数展开参数包,需要有一个同名的终止函数来终止递归,可以使用初始化列表的方式避免多定义一个同名的终止函数。

template
void printarg(T a){ cout << a << endl;}template
void expand(Args... args){ int arr[] = {(printarg(args), 0)...}; //或者改进为 std::initializer_list
{(printarg(args), 0)...};}//{(printarg(args), 0)...}会被展开为{(printarg(arg1), 0), (printarg(arg2), 0), (printarg(arg3), 0)}expand(1,2,3,"hello");

 


    这种展开参数包的方式,不需要通过递归终止函数,而是直接在expand函数体内展开,printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种处理方式的关键是逗号表达式。

    逗号表达式会按顺序执行前面的表达式,比如d = (a = b, c);b先赋值给a,接着括号中的逗号表达式返回c的值,因此d被赋值为c 

    c++11中使用列表初始化方法来初始化一个边长的数组,可以使用 int arr[] = {args...};其中args为一个变长的参数集合。

template
void print(Args... args){ int arr[] = { (args, 0)... }; //具名的args...(或者匿名的...) 代表所有的可变参数集合,可以将args和...分开,此时args表示...中每一个参数。}

 

    还可以通过lambda表达式来改进上述的列表初始化方式:

template
void expand(Args... args){ std::initializer_list
{([&]{cout << args << endl;}(), 0)...};}

 

2. 可变参数模板类

    tuple是一个可变参数模板类:

template
class tuple; 这个可变参数模板类可以携带任意类型任意个数的模板参数 std::tuple
tp1 = std::make_tuple(1); std::tuple
tp2 = std::make_tuple(1,2.4); std::tuple<> tp;

 

可变参数模板类的参数展开
(1)模板递归和特化方式展开参数包

    可变参数模板类的展开一般需要2~3个类,包括类声明和特化的模板类。如下方式定义了一个基本的可变参数模板类:

template
//前向声明struct Sum;template
//类的定义struct Sum
{ enum {value = Sum
::value + Sum
::value};};template
//递归终止类,模板参数不一定为1个,可能为0个或者2个struct Sum
{ enum{value = sizeof(Last)};};这个Sum类的作用是在编译期计算出参数包中参数类型的size之和。或者可通过std::integral_constant来修改一下:template
//前置声明struct Sum;template
//递归定义struct Sum
: std::integral_constant
::value + Sum
::value{};template
//递归终止struct Sum
: std::integral_constant
{};

 


(2)继承方式展开参数包
//整型序列的定义template
struct IndexSeq{};//继承方式,开始展开参数包template
struct MakeIndexes: MakeIndexes
{};//模板特化,终止展开参数包的条件template
struct MakeIndexes<0, Indexes...>{ typedef IndexSeq
type;};int main(){ using T = MakeIndexes<3>::type; //输出为 struct IndexSeq<0,1,2> cout << typeid(T).name() << endl; return 0;};//MakeIndexes如果不通过继承递归方式生成,可以通过using来实现。template
struct MakeIndexes{ using type = MakeIndexes
::type;};template
struct MakeIndexes<0, ...Indexes>{ using type = IndexSeq
;};

 

可以使用上述的IndexSeq来展开并打印可变模板参数,比如:template
void print_helper(IndexSeq
, std::tuple
&& tup){ print(std::get
(tup)...);}template
void print(Args... args){ print_helper(typename MakeIndexes
::type(), std::make_tuple(args...));}

 



转载于:https://www.cnblogs.com/gtarcoder/p/4810614.html

你可能感兴趣的文章
java:环境变量设置
查看>>
Servlet的学习之Response响应对象(3)
查看>>
基础知识回顾——上下文管理器
查看>>
ARM(RISC)和x86(CISC)的技术差异
查看>>
第3章 对象基础
查看>>
文件压缩与解压缩
查看>>
android 搜索自动匹配关键字并且标红
查看>>
Android ViewPager使用详解
查看>>
python爬虫之scrapy的pipeline的使用
查看>>
mysql 1366错误
查看>>
mfc 导出数据保存成excel和txt格式
查看>>
让Android中的webview支持页面中的文件上传
查看>>
UML基础
查看>>
Oracle 从Dump 文件里提取 DDL 语句 方法说明
查看>>
实现winfrom进度条及进度信息提示
查看>>
关于Spring.Net的singleton和singlecall的讨论
查看>>
vue项目目录结构
查看>>
程序员自学路上的一些感悟
查看>>
使用x64dbg分析微信聊天函数并实现发信息
查看>>
robotframework-selenium2library各个版本
查看>>