当前位置: 首页 > news >正文

C与C++中的可变参数

下面先区分一下两种“可变参数”机制,再重点讲讲 C++11 引入的可变模板参数(variadic templates)。


1. C 风格的可变参数(…)

不是的,va_list 本身并不是“可变参数”,而是 C 标准库(<stdarg.h>)提供的一个类型,用来在函数内部访问由 引入的可变参数列表。

具体来说:

  1. 可变参数函数的声明
    void foo(int fixed_arg,);
    
    这里的 (省略号)才是告诉编译器“这是个可变参数函数,可以接收不定个数的实参”。

举个最小例子:

#include <stdio.h>
#include <stdarg.h>void my_printf(const char *fmt,) {va_list ap;va_start(ap, fmt);vprintf(fmt, ap);   // 标准库 vprintf 就是接收 va_list 的版本va_end(ap);
}int main() {my_printf("x=%d, y=%.2f\n", 42, 3.14);return 0;
}
  • :声明可变参数
  • va_list ap;:申明一个遍历句柄
  • va_start(ap, fmt);:指向第 1 个可变参数
  • va_arg(ap, Type):依次读取
  • va_end(ap);:结束

总结:

  • “可变参数”是函数签名里的
  • va_list 是访问这些参数的工具类型。

缺点:

  • 不做类型检查,容易出错
  • 访问时需要 va_arg 按正确类型一一取出

2. C++11 可变模板参数(Variadic Templates)

2.1 基本语法

template<typename… Args>
void foo(Args… args) {// …
}
  • Args…:模板参数包(parameter pack)
  • args…:对应的函数形参包

你可以在函数体里用**展开(expansion)**把 args… 拆成一堆独立的参数。

2.2 递归展开(C++11/14)

演示一个打印所有参数的例子:

#include <iostream>// 终止版本
void print_all() {std::cout << "\n";
}// 递归版本
template<typename T, typename… Ts>
void print_all(T first, Ts… rest) {std::cout << first;if constexpr (sizeof(rest) > 0) {std::cout << ", ";print_all(rest…);  // 递归展开} else {std::cout << "\n";}
}int main() {print_all(1, "hello", 3.14, 'x');
}

思路:

  1. 至少拆出一个参数 first,剩下的仍是包 rest…
  2. 递归调用直到 rest… 为空

2.3 折叠表达式(Fold Expressions,C++17)

C++17 提供更简洁的“折叠表达式”来一次性展开参数包:

#include <iostream>template<typename… Ts>
void print_all(Ts const&… args) {// ( expr ⌄ ... ) 或 ( ... ⌄ expr ) 形式// 这里用逗号运算符,最后再输出换行((std::cout << args << ", "),);std::cout << "\n";
}int main() {print_all(1, "foo", 2.5, 'c');
}

常见折叠用法举例:

  • 求和: auto sum = (args + … + 0);
  • 逻辑与: auto all_ok = (args && …);
  • 输出: (std::cout << … << args);

2.4 完美转发

#include <utility>template<typename… Ts>
void logf(const char* fmt, Ts&&… args) {// 假设你有个 format_to_string(fmt, …) 函数std::string s = format_to_string(fmt, std::forward<Ts>(args));OutputDebugStringA(s.c_str());
}
  • Ts&&… args:参数包完美转发
  • std::forward<Ts>(args)…:保留左值/右值属性

3. 小结

  • C++ 的 <cstdarg>)是 C 时代遗留的,类型不安全
  • Variadic Templates(C++11 起)提供了类型安全编译期展开的可变参数
  • 配合递归、折叠表达式和完美转发,能写出既灵活又高效的通用 API。

相关文章:

  • 使用@SpringJUnitConfig注解开发遇到的空指针问题
  • 工信部发布《中国工业软件产业发展研究报告(2025)》:PLM垄断加剧,Ai为国产PLM软件发展契机
  • 基于大模型的胆囊结石全周期诊疗方案研究报告
  • voronoi图,凸壳,和早已遗忘的定不定积分
  • Flutter TCP通信
  • vscode连接不上服务器问题修复
  • golang--数据类型与存储
  • D包和模块.go
  • Spring Boot + AOP + Jasypt,3 步实现敏感数据脱敏
  • Java 实现后端调用 Chromium 浏览器无头模式截图的方案
  • Java web非Maven项目中引入EasyExcel踩坑记录
  • 批量创建tmux tmux批量
  • 深入解析 Java List 实现类的底层原理
  • 腾讯云TCCA认证考试报名 - TDSQL数据库交付运维工程师(PostgreSQL版)
  • 12.9 定时任务
  • SkyWalking 部署与应用(Windows)
  • 3DS 转换为 STP 全攻略:迪威模型网在线转换详解
  • OpenAI 如何在激烈的AI人才争夺战中抢占先机?
  • 视频或视频流和帧的关系?怎么理解?
  • MATLAB R2025a安装教程
  • 无极app定制开发公司网站模板/网络推广哪家做得比较好
  • 婚庆网站策划/免费建网站的步骤
  • 网站开发保密协议模板/长沙官网优化公司
  • 个人域名备案快的网站/免费推广有哪些
  • 武汉市城市建设委员会网站/太原网站推广公司
  • 快速网站备案多少钱/域名备案查询系统