C++ | 可变模板参数

1. 为什么需要可变模板参数?

在C++11之前,若想实现一个接受任意数量参数的函数,只能依赖va_list等C风格可变参数,但这种方式类型不安全且难以调试。例如printf函数:

printf("%d %f %s", 10, 3.14, "hello"); // 若格式字符串与参数类型不匹配,直接崩溃!

可变模板参数的诞生解决了这一问题:类型安全 + 编译期展开。它是std::make_sharedstd::tuple等工具的实现基石!


2. 基础语法:声明与展开

2.1 声明参数包

使用typename...定义模板参数包,函数参数中使用Args... args接收实参:

template <typename... Args>
void log(Args... args); // Args: 类型参数包; args: 函数参数包
2.2 混合固定参数与可变参数
template <typename T, typename... Args>
void process(T first, Args... rest); // first处理第一个参数,rest处理剩余参数

3. 参数包展开的两种核心方式

3.1 递归展开(经典方法)

通过递归模板函数逐步“剥开”参数包,需定义递归终止条件。

示例:递归打印所有参数

// 终止函数:无参数时结束递归
void print() { std::cout << "End\n"; 
}// 递归函数模板
template <typename T, typename... Args>
void print(T first, Args... rest) {std::cout << first << " ";print(rest...); // 递归调用,rest参数包被展开
}print(42, "Hello", 3.14); // 输出:42 Hello 3.14 End

关键点:递归调用时,参数包rest...会被编译器自动展开为下一个调用的参数列表。


3.2 折叠表达式(C++17起,更简洁!)

折叠表达式(Fold Expression)允许用简洁的语法对参数包进行展开操作,支持所有二元运算符。

示例1:求和所有参数

template <typename... Args>
auto sum(Args... args) {return (args + ...); // 等价于 args1 + args2 + ... + argsN
}std::cout << sum(1, 2, 3, 4); // 输出:10

示例2:打印所有参数(逗号分隔)

template <typename... Args>
void print(Args&&... args) {(std::cout << ... << args) << "\n"; // 折叠输出,展开为 ((cout << arg1) << arg2) << ...
}print("Age:", 25, ", Score:", 99.5); // 输出:Age:25, Score:99.5

优势:无需递归,代码简洁,编译效率更高!


4. 类模板中的可变参数

可变模板参数在类模板中同样大放异彩,例如实现一个简单的元组(std::tuple的简化版):

template <typename... Types>
class Tuple;// 递归继承特化:通过继承展开参数包
template <typename T, typename... Rest>
class Tuple<T, Rest...> : private Tuple<Rest...> {
public:T value;Tuple(T v, Rest... args) : value(v), Tuple<Rest...>(args...) {}
};// 基类:空参数包时终止
template <>
class Tuple<> {};// 使用
Tuple<int, std::string, double> t(10, "Test", 3.14);

解析:通过递归继承,每个Tuple层保存一个值,并继承剩余参数的Tuple基类,最终构造出一个包含所有数据的结构。


5. 实用技巧与常见操作

5.1 获取参数包大小

使用sizeof...运算符获取参数包中的参数数量:

template <typename... Args>
void logSize(Args... args) {std::cout << "参数数量:" << sizeof...(Args) << "\n";
}logSize(1, "two", 3.0); // 输出:参数数量:3
5.2 完美转发参数包

结合std::forward实现完美转发,保留参数的左值/右值特性:

template <typename... Args>
void wrapper(Args&&... args) {// 将参数包完美转发给目标函数targetFunc(std::forward<Args>(args)...);
}

6. 实际应用场景

  1. 工厂函数:如std::make_shared<T>(args...),根据参数构造对象。

  2. 格式化日志:接受任意类型和数量的参数,生成日志字符串。

  3. 元编程工具:实现std::tuplestd::variant等容器。

  4. 委托与信号槽:处理不同数量和类型的回调参数。


7. 注意事项

  • 递归终止条件:递归展开时务必定义终止函数,否则编译失败。

  • 性能开销:递归展开可能增加编译时间,折叠表达式更高效。

  • 参数顺序:混合固定参数和可变参数时,注意参数顺序。


总结

可变模板参数为C++泛型编程打开了全新的大门,结合折叠表达式和完美转发,可以优雅地处理任意数量和类型的参数。它是现代C++库开发的基石,熟练掌握这一特性,你将能写出更灵活、更强大的通用代码!

动手建议:尝试用可变模板参数实现一个类型安全的格式化函数(类似Python的format),支持format("{} + {} = {}", 2, 3, 5)的输出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/76028.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】每日一讲-朴素贝叶斯公式

文章目录 **一、朴素贝叶斯公式详解****1. 贝叶斯定理基础****2. 从贝叶斯定理到分类任务****3. 特征独立性假设****4. 条件概率的估计** **二、在AI领域的作用****1. 文本分类与自然语言处理&#xff08;NLP&#xff09;****2. 推荐系统****3. 医疗与生物信息学****4. 实时监控…

AI Agents系列之AI代理的类型

在本文中,我们将探讨不同类型的 AI 代理,包括它们的实现、实际应用、优势和局限性。从简单反射代理到多代理系统,我们将了解这些模型如何推动自动化、决策制定和智能问题解决。 文章目录 1. AI代理的类型1.1 简单反射代理1.1.1 实现**1.1.2 优势****1.1.3 局限性**1.2 基于…

C# --- IEnumerable 和 IEnumerator

C# --- IEnumerable 和 IEnumerator IEnumerableIEnumeratorIEnumerable 和 IEnumerator 的作用手动实现 IEnumerableIEnumerable vs. IQueryable为什么有了ienumerator还需要ienumerable IEnumerable 在C#中&#xff0c;IEnumerable 是一个核心接口&#xff0c;用于表示一个可…

镜舟科技助力某大型电网企业破解数据架构升级难题,打造国产化湖仓标杆

在 “十四五” 规划全面推进国产化替代的背景下&#xff0c;某大型电网企业联合镜舟科技与腾讯云&#xff0c;基于全球领先的开源分析型数据库 StarRocks 及腾讯 TBDS 大数据平台&#xff0c;构建电力行业国产化湖仓一体架构。该项目实现 PB 级电力数据的统一管理&#xff0c;为…

Spark-SQL核心编程3

数据加载与保存 通用方式&#xff1a; SparkSQL 提供了通用的保存数据和数据加载的方式。这里的通用指的是使用相同的API&#xff0c;根据不同的参数读取和保存不同格式的数据&#xff0c;SparkSQL 默认读取和保存的文件格式为parquet 数据加载方法&#xff1a; spark.read.lo…

使用HTML + CSS + JS,编写一个台球追分计分器

目录 一.代码 二.效果展示 三.该计分器的优点 一.代码 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

LLM小白自学笔记:1.两种指令微调

一、LoRA 简单来说&#xff0c;LoRA不直接调整个大模型的全部参数&#xff08;那样太费资源&#xff09;&#xff0c;而是在模型的某些层&#xff08;通常是注意力层&#xff09;加个“旁路”——两个小的矩阵&#xff08;低秩矩阵&#xff09;。训练时只更新这俩小矩阵&#x…

2026《数据结构》考研复习笔记一(C++基础知识)

C基础知识复习 一、数据类型二、修饰符和运算符三、Lambda函数和表达式四、数学函数五、字符串六、结构体 一、数据类型 1.1基本类型 基本类型 描述 字节&#xff08;位数&#xff09; 范围 char 字符类型&#xff0c;存储ASCLL字符 1&#xff08;8位&#xff09; -128…

基于骨骼识别的危险动作报警分析系统

基于骨骼识别的危险动作报警分析系统 【包含内容】 【一】项目提供完整源代码及详细注释 【二】系统设计思路与实现说明 【三】基于骨骼识别算法的实时危险行为预警方案 【技术栈】 ①&#xff1a;系统环境&#xff1a;Windows 10/11、macOS Ventura、Ubuntu 20.04 ②&#x…

【双指针】四数之和(medium)

四数之和&#xff08;medium&#xff09; 题⽬描述&#xff1a;解法&#xff08;排序 双指针&#xff09;算法思路&#xff1a; C 算法代码&#xff1a;Java 算法代码&#xff1a; 题⽬链接&#xff1a;18. 四数之和 题⽬描述&#xff1a; 给你⼀个由 n 个整数组成的数组 num…

Flask+Influxdb+grafna构建电脑性能实时监控系统

Influx下载地址&#xff0c;这里下载了以下版本influxdb-1.8.5_windows_amd64.zip 运行前需要先启动Influx数据库&#xff1a; 管理员方式运行cmd->F:->cd F:\influxdb\influxdb-1.8.5-1->influxd -config influxdb.conf&#xff0c;以influxdb.conf配置文件启动数…

如何在Keil中配置国民技术N32G系列MCU开发环境

如何在Keil及Jlink中搭建国民技术N32G系列MCU开发环境 根据自己的MCU型号&#xff08;我这里的型号是N32G452REL7&#xff09;访问国民技术官网&#xff0c;依次从N32G通用MCU-技术资源-固件和软件-软件开发套件&#xff0c;获取对应MCU型号的SDK&#xff0c;也可点击这里从网盘…

微软承认Win11出现极端错误,只能强制关机或重装系统

最近&#xff0c;不少使用 Windows 11 的用户反映&#xff0c;在系统更新后&#xff0c;“Windows Hello”突然失效&#xff0c;原本便捷的人脸识别和PIN登录功能统统无法使用。更糟的是&#xff0c;有人在重置系统后直接被挡在系统门外&#xff0c;这让人不禁发问&#xff1a;…

【android bluetooth 协议分析 02】【bluetooth hal 层详解 1】【uart 介绍】

一、什么是 UART&#xff1f; UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09; 是一种 串行通信协议&#xff0c;它的特点是通信时不需要专门的时钟信号&#xff08;叫做“异步”通信&#xff09;&#xff0c;常用于两个设备之间的简单数据通信&…

天元证券|奶粉行业结构性回暖 乳企竞速全龄化、国际化

在过去几年中&#xff0c;中国婴配粉市场经历了量价齐增&#xff0c;量减价增&#xff0c;量减价减的三个周期。历经多年行业深度洗牌与竞争格局重塑&#xff0c;2024年中国婴配粉市场回暖态势愈发清晰可辨。 日前&#xff0c;包括中国飞鹤、澳优、健合集团在内的多家奶粉股披露…

第3.1节 调用链路分析简介

调用链路&#xff08;Call Chain / Call Path&#xff09; 是程序在执行过程中&#xff0c;按照调用顺序形成的函数、模块或组件之间的依赖关系链条&#xff0c;完整记录了从程序入口到当前执行点的动态调用路径。它反映了代码执行的逻辑流程&#xff0c;是分析程序行为、调试问…

System.Security.Cryptography.CryptographicException“填充无效,无法被移除。”

这个异常通常发生在以下几种情况&#xff1a; 1.密文损坏&#xff1a;密文在传输或存储过程中被篡改或损坏。 2.密钥不匹配&#xff1a;用于解密的密钥与加密时使用的密钥不同。 3.填充模式不匹配&#xff1a;加密时使用的填充模式与解密时指定的填充模式不一致。 4.使用了不正…

【网络入侵检测】Suricata之数据包内容匹配

【作者主页】只道当时是寻常 【专栏介绍】入侵检测。专注网络、主机安全&#xff0c;欢迎关注与评论。 1. 概要 本文详细介绍了网络入侵检测系统&#xff08;如 Suricata&#xff09;中用于检查数据包或流有效载荷的 Payload 关键字。content 用于匹配数据包内容&#xff0c;默…

Spring Boot 整合 Redis 实现点赞功能:从基础到实践

在当今互联网应用开发中&#xff0c;点赞功能几乎成为了各类内容平台的标配。它不仅能增加用户与内容之间的互动&#xff0c;还能直观地反映内容的受欢迎程度。本文将详细介绍如何使用 Spring Boot 整合 Redis 来实现一个简单的文章点赞功能&#xff0c;让你轻松掌握这一实用技…

openGauss DataVec + Dify,快速搭建你的智能助手平台

在当今数字化和智能化的时代&#xff0c;大语言模型&#xff08;LLM&#xff09;的应用正以前所未有的速度改变着各个领域的工作方式和用户体验。Dify 作为一个开源的大语言模型应用开发平台&#xff0c;为开发者们提供了便捷且强大的工具&#xff0c;助力构建从基础智能体到复…