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

可变参数模板 和 折叠表达式 (C++)

可变参数模板 和 折叠表达式

  • 一、可变参数模板
  • 二、折叠表达式
    • 例子
  • 三、总结

在 C++11 中引入的可变参数模板(Variadic Templates)让我们能够编写对任意数量模板参数进行操作的通用代码,但在实现上往往要借助递归展开,写法比较繁琐。C++17 则新增了折叠表达式(Fold Expressions),让对参数包的聚合操作变得极为简洁。

一、可变参数模板

可变参数模板本质是对“参数包”(parameter pack)的支持。你可以在模板参数列表中写

template<typename... Args>

表示 Args… 是一个类型包;同理,在函数参数列表中写

void func(Args... args)

表示 args… 是一个参数包。通过 sizeof…(Args) 可以在编译期获得包的大小。

如果你想对包里每一个元素做处理,在 C++11/14 中常用的做法是递归展开:

#include <iostream>// 递归终止函数
void print_all() {std::cout << "\n";
}// 递归展开:取第一个参数,打印后递归剩下的
template<typename First, typename... Rest>
void print_all(First&& first, Rest&&... rest) {std::cout << first << ' ';print_all(std::forward<Rest>(rest)...);
}int main() {print_all(1, 2.5, "hello", 'A');
}

上述代码中,print_all(1,2.5,“hello”,‘A’) 会先匹配带 First, Rest… 的版本,打印第一个,再 “剥离” 参数包递归调用,直到包为空时调用无参版本终止。虽然能用,但每增加一种功能往往还要改终止条件,对编译器和读者都是负担。

二、折叠表达式

C++17 引入的折叠表达式,能够将二元运算符直接“折叠”到参数包上,大大简化对包聚合运算的书写。语法格式主要有以下几种(op 表示运算符,init 表示初始值,可选):

  1. Unary left fold
(... op pack)      // 相当于 ((arg1 op arg2) op arg3) ...
  1. Unary right fold
(pack op ...)      // 相当于 (arg1 op (arg2 op (arg3 ...)))
  1. Binary left fold
(init op ... op pack)  // 相当于 ((init op arg1) op arg2) ...
  1. Binary right fold
(pack op ... op init)  // 相当于 (arg1 op (arg2 op ( ... op init)))

例子

  • 以最常见的 “求和” 举例:
// 1. 最简单:对 args 求和
template<typename... Args>
auto sum(Args... args) {return (... + args);  // 等价于 ( (arg1 + arg2) + arg3 ) + ... 
}// 2. 带初始值 0
template<typename... Args>
auto sum_zero(Args... args) {return (0 + ... + args);  // 相当于 ((0 + arg1) + arg2) + ...
}int main() {auto s1 = sum(1, 2, 3, 4);      // 10auto s2 = sum_zero(1, 2, 3, 4); // 10
}
  • 再比如,把一组布尔值做“或”运算:
template<typename... Bools>
bool any(Bools... bs) {return (... || bs);  // 如果任一 bs 为 true,结果为 true
}
  • 折叠表达式不仅限于算术或逻辑运算,任何支持的二元运算符都能用,比如逗号运算符(用于依次调用函数)、位运算、比较运算等。
#include <iostream>// 用逗号运算符依次调用 print
template<typename... Args>
void print_all(Args&&... args) {( (std::cout << args << ' '), ... );  std::cout << '\n';
}

上面这行 ( (std::cout << args << ’ ‘), … ); 就是对参数包做了逗号折叠:依次执行 std::cout<<arg1<<’ ‘, std::cout<<arg2<<’ ’ …,最后一个逗号表达式的值被忽略。

三、总结

C++17 的折叠表达式配合可变参数模板,让对任意数量参数进行聚合操作时,代码更加简洁、直观,完全摆脱了递归终止函数的模板噩梦。可以根据是否需要初始值,选择带或不带 init 的左折叠或右折叠形式;常见模式如下:

(... op pack)          // 无初始值,左折叠
(pack op ...)          // 无初始值,右折叠
(init op ... op pack)  // 左折叠,带初始值
(pack op ... op init)  // 右折叠,带初始值
http://www.xdnf.cn/news/91387.html

相关文章:

  • 人工智能-模型评价与优化(过拟合与欠拟合,数据分离与混淆矩阵,模型优化,实战)
  • 《AI大模型应知应会100篇》第32篇:大模型与医疗健康:辅助诊断的可能性与风险
  • RAG进阶:Embedding Models嵌入式模型原理和选择
  • 【网络应用程序设计】实验一:本地机上的聊天室
  • 1.HTTP协议与RESTful设计
  • char32_t、char16_t、wchar_t 用于 c++ 语言里存储 unicode 编码的字符,给出它们的具体定义
  • 【武汉理工大学第四届ACM校赛】copy
  • 凡清亮相第十五届北京国际电影节电影嘉年华,用音乐致敬青春与梦想
  • 调和平均数通俗易懂的解释以及为什么这样定义,有什么用
  • 《 C++ 点滴漫谈: 三十四 》从重复到泛型,C++ 函数模板的诞生之路
  • 客户对质量不满意,如何快速响应?
  • ycsb性能测试的优缺点
  • GRS认证有什么要求?GRS认证要审核多久,GRS认证流程
  • 旅游行业路线预定定制旅游小程序开发
  • vivado XMP使用
  • 2023蓝帽杯初赛内存取证-1
  • ROS2 安装详细教程,Ubuntu 22.04.5 LTS 64 位 操作系统
  • Nacos 是如何实现 Raft 协议的?Raft 协议的关键组件和流程是什么?
  • Java基础复习(JavaSE进阶)第八章 多线程
  • C++静态与动态联编区别解析
  • Vue3简介
  • TDengine 查询引擎设计
  • 滑动模式观测器(Sliding mode observer)
  • 机器视觉的液晶屏点胶应用
  • 飞搭系列 | 组件增加标记,提升用户体验
  • android开发-BuildConfig无法生成
  • [Java · 铢积寸累] 数据结构 — 二维数组 - 概念引入
  • 潮玩+智显 |电子价签演绎潮玩信息智显的百变状态
  • Linux系统之----进程的概念
  • GpuGeek:以弹性算力与全栈服务赋能产业智能升级