【C++】C语言可变函数参数 | C++11可变参数模板

news/2024/7/24 6:31:59 标签: c++, c语言, 开发语言

文章目录

      • C语言的可变函数参数
        • 遍历`va_list`逐个取出参数
      • C++可变模板参数
        • 递归展开模板参数包示例代码
        • 逗号表达式展开参数包
      • 总结

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数。

C语言的可变函数参数和C++的可变参数模板有相似之处

C语言的可变函数参数

C语言中,可变函数参数是通过stdarg.h头文件中的宏和函数来实现的。标准库中的printf、scanf,就是用可变函数参数的原理实现的。

  • va_list: 定义一个变量,用于存储可变参数的信息。
  • va_start(va_list ap, last_arg): 初始化va_list,使其指向可变参数列表中的第一个参数。
  • va_arg(va_list ap, type): 获取可变参数列表中的下一个参数的值,同时将va_list指向下一个参数。
  • va_end(va_list ap): 清理va_list,使其无效。
遍历va_list逐个取出参数
#include <stdarg.h>
#include <stdio.h>

int sum(int count, ...) 
{
    int result = 0;
    va_list args;
    va_start(args, count);

    for (int i = 0; i < count; ++i) 
    {
        result += va_arg(args, int);
    }

    va_end(args);
    return result;
}

int main() 
{
    int total = sum(3, 10, 20, 30);
    printf("Sum: %d\n", total);

    return 0;
}

C++可变模板参数

C++中,可变模板参数是通过模板参数包来实现的。

  • Args是一个模板参数包,而args是一个函数形参参数包。
  • 声明一个参数包Args ...args,这个参数包中可以包含0到任意个模板参数。
  • 使用sizeof...(Args),可以 获取模板参数包中的参数数量。

下面是一个简单的可变参数的函数模板:

template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数
包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,
只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特
点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变
参数,所以我们的用一些奇招来一一获取参数包的值。

递归展开模板参数包示例代码
  • 利用递归,展开模板参数包,可以在编译时处理不同数量的参数。
#include <iostream>

// 递归展开模板参数包
// 递归终止函数
template<typename T>
void print(T value) 
{
    std::cout << value << std::endl;
}

// 展开函数
template<typename T, typename... Args>
void print(T first, Args... args) 
{
    std::cout << first << ", ";
    print(args...);
}

int main() 
{
    print(1, 2.5, "Hello", 'a');

    return 0;
}
逗号表达式展开参数包

如果参数包中各个参数的类型都是整型,那么也可以把这个参数包放到列表当中初始化这个整型数组,此时参数包中参数就放到数组中了:

//展开函数
template<class ...Args>
void ShowList(Args... args)
{
	int arr[] = { args... }; //列表初始化
	//打印参数包中的各个参数
	for (auto e : arr)
	{
		cout << e << " ";
	}
	cout << endl;
}

int main()
{
	ShowList(1, 2, 3, 4);             //正确
	ShowList("hello", 1, "world", 2); //错误:数组arr只能存放相同类型元素int
	ShowList();                       //错误:不能分配大小为0的数组
	return 0;
}

虽然我们不能用不同类型的参数去初始化一个整型数组,但我们可以借助逗号表达式。

  • 逗号表达式会从左到右依次计算各个表达式,并且将最后一个表达式的值作为返回值进行返回。
  • 将逗号表达式的最后一个表达式设置为一个整型值,确保逗号表达式返回的是一个整型值。
  • 将处理参数包中参数的动作封装成一个函数,将该函数的调用作为逗号表达式的第一个表达式。
  • {(printarg(args), 0)...}将会展开成{(printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... }
  • 这样一来,在执行逗号表达式时就会先调用处理函数处理对应的参数,然后再将逗号表达式中的最后一个整型值作为返回值来初始化整型数组。

比如:

//支持无参调用
void ShowList()
{
	cout << endl;
}
//处理函数
template<class T>
void PrintArg(const T& t)
{
	cout << t << " ";
}
//展开函数
template<class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... }; //列表初始化+逗号表达式
	cout << endl;
}

总结

  • C语言使用stdarg.h来实现可变函数参数,通过va_listva_startva_argva_end宏进行操作。
  • C++使用模板参数包来实现可变模板参数,可以利用递归、逗号表达式展开参数包处理不同数量的参数。

http://www.niftyadmin.cn/n/5389477.html

相关文章

详解编译和链接!

目录 1. 翻译环境和运行环境 2. 翻译环境 2.1 预处理 2.2 编译 2.3 汇编 2.4 链接 3. 运行环境 4.完结散花 悟已往之不谏&#xff0c;知来者犹可追 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们…

宝塔面板安装了mysql5.7和phpMyadmin,但是访问phpMyadmin时提示502 Bad Gateway

操作流程截图如下&#xff1a; 原因是没有选择php版本 选择php版本 下一页找到phpMyAdmin&#xff0c;选择设置 目前只有纯净态&#xff0c;说明没有php环境&#xff0c;前去安装php环境 点击安装&#xff0c;选择版本&#xff0c;这里选择的是7.4版本&#xff0c;编译安…

机器学习系列-生成模型和判别模型

文章目录 前言1.生成模型和判别模型2.常见的生成模型和判别模型3.判别式模型和生成式模型的优缺点 前言 监督学习模型可以分为生成模型(generative model)和判别模型(discriminative model) 1.生成模型和判别模型 判别模型由数据直接学习决策函数 f ( x ) f(x) f(x)或者条件…

第十二天-ppt的操作

目录 创建ppt文档 安装 使用 段落的使用 段落添加数据 段落中定义多个段落 自定义段落 ppt插入表表格 PPT插入图片 读取ppt 读取ppt整体对象 ​编辑 获取ppt文本 获取表格内容 创建ppt文档 安装 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple python…

jenkins编译使用nohup部署进程到后台失败,解决方法

在shell脚本中加入BUILD_IDdontKillMe server为二进制文件 #!/bin/bashBUILD_IDdontKillMenohup ./server & 原理&#xff1a;jenkins默认会在构建完成后杀掉构建过程中shell命令触发的衍生进程。jenkins根据BUILD_ID识别某个进程是否为构建过程的衍生进程&#xff0c;故…

SpringBoot项目实现文件上传,MINIO+OSS阿里云

MINIO 安装以及部署 官网&#xff1a;MinIO | Code and downloads to create high performance object storage 下载后是一个minio.exe的文件&#xff0c;可以先创一个文件夹来存放数据以及文件 在文件的目录下cmd进入控制台 minio.exe server data 启动成功后控制台会打印账…

Kotlin filterIsInstance filterNotNull forEach

Kotlin filterIsInstance filterNotNull forEach fun main(args: Array<String>) {val i1 MyItem(1, 1)val i2: MyItem? nullval i3: Int 3val i4 "4"val i5 nullval i6 MyItem(6, 6)val list mutableListOf<Any?>(i1, i2, i3, i4, i5, i6)lis…

微服务篇之分布式事务

一、Seata架构 Seata事务管理中有三个重要的角色&#xff1a; TC (Transaction Coordinator) - 事务协调者&#xff1a;维护全局和分支事务的状态&#xff0c;协调全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器&#xff1a;定义全局事务的范围、开始全局事务、…