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

C++模板编程——可变参函数模板之折叠表达式

目录

1. 什么是折叠表达式

2. 一元左折

3. 一元右折

4. 二元左折

5. 二元右折

6. 后记


上一节主要讲解了可变参函数模板和参数包展开,这一节主要讲一下折叠表达式。

1. 什么是折叠表达式

折叠表达式是C++17中引入的概念,引入折叠表达式的目的是为了计算某个值。这跟可变参有关。

上一节提到了递归展开参数包,通过递归调用的方式可以一个一个地拿到每一个参数并进行处理。

折叠表达式允许我们一次性让所有参数参与计算

让我们来看代码感受一下:

#include <iostream>

using namespace std;

template<typename... T>
auto add_all(T... args)
{
	return (... + args); // 圆括号不可以省略,圆括号中的内容就是折叠表达式
}

int main()
{
	cout << add_all(1, 2, 3, 4, 5) << endl;
}

运行结果如下图所示:

从结果可以看到,add_all(1,2,3,4,5)的结果是(1+2+3+4+5)的求和结果。

return 语句后面的圆括号中的内容就是折叠表达式


折叠表达式一般有4种格式,每种格式都是用圆括号括起来。

  • 一元左折
  • 一元右折
  • 二元左折
  • 二元右折

左折就是参数从左侧开始计算,右折就是参数从右侧开始计算。

现在不理解没关系,下面会详细介绍。

2. 一元左折

一元左折的格式为:(... 运算符 一包参数)

计算过程为:(((参数1 运算符 参数2) 运算符 参数3) ... 运算符 参数N)

这种形式的三个点在开头,中间是运算符,一包参数在右侧,整体用圆括号括起来的形式就是一元左折。

上面代码中演示的就是一元左折的形式。下面再举个别的例子。

#include <iostream>

using namespace std;


template<typename... T>
auto mul_all(T... args)
{
	return (... * args);
}

int main()
{
	cout << mul_all(1, 2, 3) << endl;// ((1+2)+3) = 6
}

运行结果如下图所示:

mul_all函数中的(... * args)就是一元左折。

理解起来应该很容易,其中运算符可以换成其他的,可以编写代码尝试一下。

一元左折就先介绍到这里。

3. 一元右折

一元左折的格式为:(一包参数 运算符 ...)

计算过程为:(参数1 ... (数N-2 运算符(参数N-1 运算符 参数N)))

一元右折和一元左折的格式非常相似,只是一包参数的位置和...的位置反了过来。

计算过程变成了从右侧开始计算。

这里再强调一次,左折就是参数从左侧开始计算,右折就是参数从右侧开始计算。

下面再给一个具体的代码示例:

#include <iostream>

using namespace std;

template<typename... T>
auto sub_all_left(T... args) // 一元左折
{
	return (... - args);
}

template<typename... T>
auto sub_all_right(T... args) // 一元右折
{
	return (args - ...);
}

int main()
{
	cout << sub_all_left(1, 2, 3) << endl; // ((1-2)-3) = -4
	cout << sub_all_right(1, 2, 3) << endl; // (1-(2-3)) = 2
}

这里展示了一元左折和一元右折的减法运算符的实现,运行结果如下图所示:

代码比较好理解,不太理解的话仔细看看注释和前面的文字,帮助理解。

一元右折就介绍到这里。

4. 二元左折

二元左折的格式为:(init 运算符 ... 运算符 一包参数)

计算过程为:((((init 运算符 参数1) 运算符 参数2) 运算符 参数3) ... 运算符 参数N)

其中,init相当于一个初始值,也可以是一个对象什么的,只要能参与运算符的运算就可以。

下面看一段代码帮助理解:

#include <iostream>

using namespace std;


template<typename U, typename... T>
auto sub_all_left_with_init(U init, T... args) // 二元左折
{
	return (init - ... - args);
}


int main()
{
	int init = 10;
	cout << sub_all_left_with_init(init, 2, 3, 4) << endl; // ((((init-2))-3)-4)=1
}

运行结果如下图所示:


上面那个例子看完之后,再来看看这个例子,启发一下。

#include <iostream>
using namespace std;

template<typename... T>
void print_all(T... args)
{
	(cout << ... << args) << endl;
}

int main()
{
	print_all(1, 2, 3, 4, 5); // 12345
}

运行结果如下图所示:

因为cout << arg的返回值仍然是cout,所以二元左折可以进行,好好体会一下。

5. 二元右折

二元左折的格式为:(一包参数 运算符 ... 运算符 init)

计算过程为:(参数1 ... (参数N-2 运算符 (参数N-1 运算符 (参数N 运算符 init ))))

跟二元左折很类似,直接上代码感受一下吧:

#include <iostream>

using namespace std;

template<typename U, typename... T>
auto sub_all_right_with_init(U init, T... args) // 二元右折
{
	return (args - ... - init);
}

int main()
{
	int init = 10;
	cout << sub_all_right_with_init(init, 2, 3) << endl;//(2-(3-init)) = 9
}

运行结果如图所示:

6. 后记

到这里四种折叠表达式就介绍完毕了,每种都给了讲解和代码示例。有不懂的地方欢迎评论区提问


http://www.kler.cn/a/529920.html

相关文章:

  • LeetCode 404.左叶子之和
  • FPGA 使用 CLOCK_DEDICATED_ROUTE 约束
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.5 高级索引应用:图像处理中的区域提取
  • 基于Python的简单企业维修管理系统的设计与实现
  • 2.1刷题日记
  • el-table组件样式如何二次修改?
  • 使用 DeepSeek-R1 与 AnythingLLM 搭建本地知识库
  • IM 即时通讯系统-46-OpenIM 提供了专为开发者设计的开源即时通讯解决方案
  • bat脚本实现自动化漏洞挖掘
  • 【零基础学JAVA】数据类型
  • 20250202在Ubuntu22.04下使用Guvcview录像的时候降噪
  • Java/Kotlin HashMap 等集合引发 ConcurrentModificationException
  • [HOT 100] 0167. 两数之和 ||
  • Elasticsearch 指南 [8.17] | Search APIs
  • python算法和数据结构刷题[6]:二叉树、堆、BFS\DFS
  • 机器学习算法在网络安全中的实践
  • 系统学习算法: 专题八 二叉树中的深搜
  • Node.js——异步编程(异步:阻塞与非阻塞、JavaScript执行机制、callBack hell 回调地狱,Promise、Async await)
  • Stable Diffusion创始人:DeepSeek没有抄袭!
  • 深入浅出并查集(不相交集合实现思路)
  • 2025年02月02日Github流行趋势
  • 【最长不下降子序列——树状数组、线段树、LIS】
  • 图像分割任务的数据预处理
  • 012-51单片机CLD1602显示万年历+闹钟+农历+整点报时
  • XML DOM 浏览器差异
  • 【AI】人工智能没那么神秘!