C/C++中的可变参数和可变参数模板
1、说明
不谈官方定义,就从个人理解上说,可变参数 就是函数传参的时候,不确定传入参数的数量和类型,从而动态地在函数内部处理,优点是,函数调用时比较灵活
2、C语言中的可变参数
C语言中一般使用宏定义实现可变参数,先看一个示例:
#include <stdarg.h>
void func(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
auto a = va_arg(ap, int);
auto b = va_arg(ap, double);
auto c = va_arg(ap, char*);
cout << a << ", " << b << ", " << c << endl;
va_end(ap);
}
int main()
{
func("%d %f %s\n", 1, 2.0f, "hello world");
return 0;
}
这是一个很常见的C语言的可变参数的使用,va_start 用于初始化 va_list 变量,va_arg 用于提取可变参数,va_end 用于释放 va_list
这个示例可以用在一般函数上,无法使用在宏定义中,如果一定要在宏定义中使用,需要配合 __VA_ARGS__,示例如下:
//#define CALC(fmt, ...) func(fmt, ...) //错误的使用
#define CALC(fmt, ...) func(fmt, __VA_ARGS__)
int main()
{
CALC("%d %f %s\n", 1, 2.0f, "hello world");
return 0;
}
3、C++中的可变参数模板
C++11 中引入了新的功能,可变参数模版,语法如下:
template <typename T, typename ... Args>
void func(T t,Args ... args);
这里面,Args 称之为模板参数包(template parameter pack),表示模板参数位置上的变长参数,
args 称之为函数参数包(function parameter pack),表示函数参数位置上的变长参数
可以使用 sizeof...() 获取可变参数数目
先看一个示例:
template<typename... Args>
void print(Args... args)
{
int num = sizeof...(args);
}
int main()
{
print(1, 2, "123", 4);
return 0;
}
执行结果是:
4
2.1、使用递归的方式遍历
可变参数一般使用递归的方式进行遍历,利用模板的推导机制,每次从可变参数中取出第一个元素,直到包为空
缺点:递归毕竟是使用栈内存,过多的递归层级容易导致爆栈的发生
示例代码如下:
void printf()
{
cout << "end" << endl;
}
template<typename T, typename... Args>
void print(const T &value, Args... args)
{
cout << value << endl;
print(args...);
}
int main()
{
print(1, 2, "333", 4);
return 0;
}
结果输出如下:
1
2
333
4
end
这里不是很好理解,我们可以自己理解 print() 方法的每一次调用
- main函数中第一次调用,value为1, args有2、"333和4三个值,输出1;
- 第一次递归,即print中调用print,value为2,args有“333”和4两个值,输出2;
- 第二次递归,即print中调用print,value为“333”,args为4,输出“333”;
- 第三次递归,即print中调用print,value为4,args无值,输出4;
- 此时,args因为无值,print(args...) 语句调用的就不再是模板函数,而是第一行的 print(),输出end;
所以,很好理解,为什么要先定义一个同名的函数,就是为了等可变参数经过几次推导之后,没有值的情况出现;
当然,递归遍历也可以这么写:
template<typename T>
void print(T value)
{
cout << "end:" << value << endl;
}
template<typename T, typename... Args>
void print(const T &value, Args... args)
{
cout << value << endl;
print(args...);
}
int main()
{
print(1, 2, "333", 4);
return 0;
}
结果输出:
1
2
333
end:4
这和第一个例子只有些许区别,函数调用如下:
- main函数中第一次调用,value为1, args有2、"333和4三个值,输出1;
- 第一次递归,即print中调用print,value为2,args有“333”和4两个值,输出2;
- 第二次递归,即print中调用print,value为“333”,args为4,输出“333”;
- 此时,args为4,print(args...) 语句调用的就不再是模板函数,而是第一行的 print(4),输出end:4;
2.2、使用非递归的方式遍历
利用 std::initializer_list ,即初始化列表展开可变参数
示例1,使用展开函数处理参数:
template<typename T>
void run(const T &t)
{
cout << t << endl;
}
template<typename... Args>
void print(Args... args)
{
std::initializer_list<int>{(run(args), 0)...};
}
int main()
{
print(1, 2, "333as", 4);
return 0;
}
示例2,使用lambda:
template<typename... Args>
void print(Args... args)
{
std::initializer_list<int>{([&]
{ cout << args << endl; }(), 0)...};
}
int main()
{
print(1, 2, "333as", 4);
return 0;
}
C/C++中的可变参数和可变参数模板的更多相关文章
- python中的函数的参数和可变参数
最近在搞python的过程中需要用到给函数传可变参数..所以去网上找前人的帖子学习了一下 为了尊重原作者,这里附上链接:http://www.cnblogs.com/tqsummer/archive/ ...
- Java中不定项参数(可变参数)的作用和使用方式
引言: 我们在编写方法的过程中,可能会遇见一个方法有不确定参数个数的情况.一般我们会用方法重载来解决问题: //方法重载,解决参数个数不确定问题 public void method(); publi ...
- C++ 11可变参数接口设计在模板编程中应用的一点点总结
概述 本人对模板编程的应用并非很深,若要用一句话总结我个人对模板编程的理解,我想说的是:模板编程是对类定义的弱化. 如何理解“类定义的弱化”? 一个完整的类有如下几部分组成: 类的名称: 类的成员变量 ...
- Python中函数的参数传递与可变长参数
转自旭东的博客原文 Python中函数的参数传递与可变长参数 Python中传递参数有以下几种类型: (1)像C++一样的默认缺省函数 (2)根据参数名传参数 (3)可变长度参数 示例如下: (1)默 ...
- .NET框架- in ,out, ref , paras使用的代码总结 C#中in,out,ref的作用 C#需知--长度可变参数--Params C#中的 具名参数 和 可选参数 DEMO
C#.net 提供的4个关键字,in,out,ref,paras开发中会经常用到,那么它们如何使用呢? 又有什么区别? 1 in in只用在委托和接口中: 例子: 1 2 3 4 5 6 7 8 9 ...
- python函数中的位置参数、默认参数、关键字参数、可变参数区别
一.位置参数 调用函数时根据函数定义的参数位置来传递参数. #!/usr/bin/env python # coding=utf-8 def print_hello(name, sex): sex_d ...
- Python中为什么不能用可变对象作为默认参数的值
def func(numbers = [], num=1): numbers.append(num) for number in numbers: print(number) func() >& ...
- Python中的*可变参数与**关键字参数
1.定义了一个需要两个参数的函数 def print_str(first, second): print first print second if __name__ == "__main_ ...
- Java中不定项参数(可变参数)的使用
Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理. 注意事项: 1)不定项参数必须放在参数列表最后一个. 2)不定项参数只能有一个(多 ...
- c#基础之长度可变类型相同的参数列表
为了简化编码,c#提供了一个特殊的关键字params,允许在调用方法时提供数量可变的实参,而不是由方法实现固定好的形参数量.先看代码吧. using System; using System.Linq ...
随机推荐
- Woodpecker CI 设计分析|一个 Go 编写的开源持续集成引擎
一.前言 大家好,这里是白泽.随着 Go 语言在云原生领域大放异彩,开发者逐渐将目光转移到了这门语言上,而容器则是云原生时代最核心的载体. <Woodpecker CI 设计分析>系列文章 ...
- #2059:龟兔赛跑(动态规划dp)
Problem Description 据说在很久很久以前,可怜的兔子经历了人生中最大的打击--赛跑输给乌龟后,心中郁闷,发誓要报仇雪恨,于是躲进了杭州下沙某农业园卧薪尝胆潜心修炼,终于练成了绝技,能 ...
- Spring 学习笔记(5)AOP
本文介绍 Spring 中 AOP 的原理及使用方式. Spring AOP 简介 如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切 ...
- BZOJ 3450 - Tyvj1952 Easy (期望DP)
描述 某一天 WJMZBMR 在打 osu~~~ 但是他太弱逼了,有些地方完全靠运气:( 我们来简化一下这个游戏的规则: 有 \(n(n\le 300000)\) 次点击要做,成功了就是 o,失败了就 ...
- Python 在VSCode中使用
PyCharm到期了,所以打算换成VS Code(宇宙最好用编辑器?)试一试.但是下载之后不太会配置,网上不少的文章也比较旧了,所以打算自己看着官方教程配置一下.(VSCode版本:1.49.2,Py ...
- 你折腾一天都装不上的插件,函数计算部署 Stable Diffusion 都内置了
在进行函数计算 Stable Diffusion 答疑的过程中,遇到很多同学在装一些插件的过程中遇到了难题,有一些需要安装一些依赖,有一些需要写一些代码,很多时候安装一个插件就能折腾几天,我们收集了很 ...
- 活动回顾|阿里云 Serverless 技术实战与创新成都站回放&PPT下载
7月29日"阿里云 Serverless 技术实战与创新"成都站圆满落幕.活动受众以关注 Serverless 技术的开发者.企业决策人.云原生领域创业者为主,活动形式为演讲.动手 ...
- 深度 | 新兴软件研发范式崛起,云计算全面走向 Serverless 化
11月3日,2022 杭州 · 云栖大会上,阿里云智能总裁张建锋表示,以云为核心的新型计算体系正在形成,软件研发范式正在发生新的变革,Serverless 是其中最重要的趋势之一,阿里云将坚定推进核心 ...
- 定期发送邮件功能-outlook与腾讯邮箱
一.背景:定期发送邮件功能挺好用的,可以帮忙我们在特殊的时间点发送邮件,以及实现无人推送的功能 二.outlook的实现1.首先编辑好邮件保存至草稿 2.选项-延迟传递,设置不早于传递的时间,点击发送 ...
- socket TCP DPT 网络编程
复习: ARP协议: 广播和单播 通过ip地址获得mac地址 机器A发起一个arp请求(只包含A的ip地址) 交换机接收到请求,广播这条消息 所有的机器都会接受到这条请求,只有需要寻找的机器B的ip地 ...