折叠表达式(fold expression)

折叠表达式是C++17新引进的语法特性。使用折叠表达式可以简化对C++11中引入的参数包的处理,从而在某些情况下避免使用递归。

语法形式

折叠表达式共有四种语法形式。分别为一元的左折叠和右折叠,以及二元的左折叠和右折叠。

  1. 一元右折叠(unary right fold)
    ( pack op ... )
    一元右折叠(E op ...)展开之后变为 E1 op (... op (EN-1 op EN))
  2. 一元左折叠(unary left fold)
    ( ... op pack )
    一元左折叠(... op E)展开之后变为 ((E1 op E2) op ...) op EN
  3. 二元右折叠(binary right fold)
    ( pack op ... op init )
    二元右折叠(E op ... op I)展开之后变为 E1 op (... op (EN−1 op (EN op I)))
  4. 二元左折叠(binary left fold)
    ( init op ... op pack )
    二元左折叠(I op ... op E)展开之后变为 (((I op E1) op E2) op ...) op EN

语法形式中的op代表运算符,pack代表参数包,init代表初始值。
初始值在右边的为右折叠,展开之后从右边开始折叠。而初始值在左边的为左折叠,展开之后从左边开始折叠。
不指定初始值的为一元折叠表达式,而指定初始值的为二元折叠表达式。
当一元折叠表达式中的参数包为空时,只有三个运算符(&& || 以及逗号)有缺省值,其中&&的缺省值为true,||的缺省值为false,逗号的缺省值为void()。

下面看看具体应用

求和

假设我们想写一个能接受一个及以上参数的求和函数,

#include <iostream>
using namespace std;template<typename First>
First sum1(First&& value)
{  return value;
}template<typename First, typename Second, typename... Rest>
First sum1(First&& first, Second&& second, Rest&&... rest)
{  return sum1(first + second, forward<Rest>(rest)...);
}template<typename First, typename... Rest>
First sum2(First&& first, Rest&&... rest)
{return (first + ... + rest);
}int main()
{cout << sum1(1) << sum1(1, 2) << sum1(1, 2, 3) << endl; // 136cout << sum2(1) << sum2(1, 2) << sum2(1, 2, 3) << endl; // 136
}
  • 在C++17之前,求和函数sum1的实现必须分成两个部分。
    其中4到8行的sum1函数用于处理一个参数的情况。而10到14行的sum1函数用于处理两个及以上参数的情况。
    当参数个数大于一个时,10到14行的sum1函数将前两个参数相加,然后递归调用自身。
    当参数个数只有一个时,4到8行的sum1函数将此参数返回,完成求和。
    sum1(1, 2, 3) = sum1(1+2, 3) = sum1(3, 3) = sum1(3+3) = sum1(6) = 6
  • 而在C++17之后,由于有了折叠表达式这个新特性,求和函数sum2不再需要处理特殊情况,实现大为简化。
    sum2(1, 2, 3) = (1 + ... + pack(2, 3)) = (1+2) + 3 = 6
    这里sum2的实现所采用的是二元左折叠。

“与”和“或”

#include <iostream>
using namespace std;template<typename... Args>
bool all(Args... args) {return (... && args);}
template<typename... Args>
bool any(Args... args) {return (... || args);}int main()
{cout << boolalpha << all(true, false, true) << endl; // falsecout << boolalpha << any(true, false, true) << endl; // truecout << boolalpha << all() << endl; // truecout << boolalpha << any() << endl; // false
}
  • 在这里all和any函数分别实现了不特定多数布尔值的与和或的运算。这两个函数的实现均采用了一元左折叠。
    all(true, false, true) = (... && pack(true, false, true)) = (true && false) && true = false
    any(true, false, true) = (... || pack(true, false, true)) = (true || false) || true = true
  • 当一元折叠表达式中的参数包为空时,&&的缺省值为true,而||的缺省值为false。
    all() = (... && pack()) = true
    any() = (... || pack()) = false

“打印”和“调用”

#include <iostream>
using namespace std;template<typename... Ts>
void printAll(Ts&&... mXs)
{(cout << ... << mXs) << endl;
}template<typename TF, typename... Ts>
void forArgs(TF&& mFn, Ts&&... mXs)
{(mFn(mXs), ...);
}int main()
{printAll(3, 4.0, "5"); // 345printAll(); // 空行forArgs([](auto a){cout << a;}, 3, 4.0, "5"); // 345forArgs([](auto a){cout << a;}); // 空操作
}
  • printAll函数实现了对不特定多数值的打印输出。该函数的实现采用了二元左折叠。
    printAll(3, 4.0, "5")
    = (cout << ... << pack(3, 4.0, "5")) << endl
    = ((cout << 3) << 4.0) << "5" << endl
    = 打印345并换行
  • 当二元折叠表达式的参数包为空时,其计算结果为该二元折叠表达式中所预设的初始值。
    printAll()
    = (cout << ... << pack()) << endl
    = cout << endl
    = 空行
  • forArgs函数实现了依次使用多个参数调用某个单参数函数的功能。该函数的实现采用了一元右折叠。
    forArgs([](auto a){cout << a;}, 3, 4.0, "5")
    = ([](auto a){cout << a;}(pack(3, 4.0, "5")), ...)
    = [](auto a){cout << a;}(3), ([](auto a){cout << a;}(4.0), ([](auto a){cout << a;}("5")))
    = 打印345
  • 当使用逗号的一元折叠表达式中的参数包为空时,其计算结果为标准规定的缺省值void()。
    forArgs([](auto a){cout << a;})
    = ([](auto a){cout << a;}(pack()), ...)
    = void()

C++17尝鲜:fold expression(折叠表达式)相关推荐

  1. C++17尝鲜:类模板中的模板参数自动推导

    目录 模板参数自动推导 示例 自定义类模板中的应用 Automatic deduction guides(自动推断向导) 模板参数自动推导 在C++17之前,类模板构造器的模板参数是不能像函数模板的模 ...

  2. C++17 fold expression

    1.简介 C++11 增加了一个新特性变参模板(variadic template),它可以接受任意个模版参数,参数包不能直接展开,需要通过一些特殊的方法,比如函数参数包的展开可以使用递归方式或者逗号 ...

  3. C++17之折叠表达式

    从c++ 17起,有一个特性可以计算在一个参数包的所有参数上使用二进制运算符的结果(带有一个可选的初值). 例如,下面的函数返回所有传递参数的和: #include <iostream> ...

  4. 魅族android11,安卓11版本升级了,魅族17系列开启内测尝鲜,这功能太棒了

    原标题:安卓11版本升级了,魅族17系列开启内测尝鲜,这功能太棒了 此前 12 月 22 日,魅族 Flyme 官方宣布,魅族 17 系列即将迎来基于 Android 11 系统底层的 Flyme 新 ...

  5. hexo(sakura)给博客增添侧边栏(回到顶部,跳转评论,深色模式,播放音乐)Valine-1.4.4新版本尝鲜+个性制定(表情包、qq头像、UI样式)

    文章目录 hexo(sakura)给博客增添侧边栏(回到顶部,跳转评论,深色模式,播放音乐) 原理 直接使用 Valine-1.4.4新版本尝鲜+个性制定(表情包.qq头像.UI样式) 总结 本文全是 ...

  6. JDK 16 昨日正式发布,新特性实践尝鲜来啦!

    JDK 16在2021年2月18日已完成最终候选版本,并于2021年3月16日正式发布.和JDK 15一样,JDK 16也会是一个短期版本,仅支持六个月.而计划在2021年9月发布的JDK 17将会是 ...

  7. android oppo 权限,OPPO Reno可尝鲜Android Q:教程如下

    原标题:OPPO Reno可尝鲜Android Q:教程如下 5月8日凌晨,Android Q在谷歌I/O开发者大会上正式亮相.在I/O大会现场,谷歌公布了首批Android Q升级名单,其中OPPO ...

  8. 前端每周清单第 49 期:Webpack 4 Beta 尝鲜,React Windowing 与 setState 分析

    前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点:分为新闻热点.开发教程.工程实践.深度阅读.开源项目.巅峰人生等栏目.欢迎关注[前端之巅]微信公众号(ID: fron ...

  9. Go 1.18.1 Beta 尝鲜 泛型 FuzzTest workspace mode

    Go 1.18.1 Beta 尝鲜 昨天,go 终于发布了 1.18 的 beta 版本, 带来了大家期待已久的泛型,抓紧时间康康能不能赶上热乎的. 下载地址 根据社区昨天发的 Go 1.18 Bet ...

  10. 带你尝鲜LiteOS 组件EasyFlash

    摘要:EasyFlash是一个开源的轻量级嵌入式闪存库. 本文分享自华为云社区<LiteOS组件尝鲜-玩转EasyFlash>,作者:Lionlace . 基本介绍 EasyFlash是一 ...

最新文章

  1. mysql 创建库 5.7_MySQL数据库之MySQL5.7创建用户时报错
  2. c语言万年历闹钟程序,c语言编写的万年历 有平年闰年 有闹钟功能.docx
  3. VTK:提取可见细胞用法实战
  4. 信号量进程同步与互斥
  5. 初识Node.js之Node.js与java作为后台服务器的对比
  6. 高颜值网易云音乐第三方播放器 YesPlayMusic Mac中文版 支持m1
  7. Linux下安装JDK7和TomCat7
  8. python实现的json数据以HTTP GET,POST,PUT,DELETE方式页面请求
  9. 理财产品利息一直降低是什么原因?
  10. Linux基于升序链表的定时器
  11. 2020年最新世界地图_2020年标准地图发布,为何要发布新版标准地图?
  12. java的第十一章总结(枚举,泛型)
  13. linux系统怎么拨号上网,linux配置上网 linux adsl拨号上网设置
  14. 【愚公系列】2023年02月 .NET CORE工具案例-Lunar日历转换
  15. Intel GPA 参数
  16. Leetcode 208、实现Trie(前缀树)
  17. 进程、线程和协程(进阶篇)
  18. 什么才是真正的云主机?辨别真假云主机
  19. Flutter调用阿里云识别银行卡
  20. 品牌入局B站 不可忽视的内容营销趋势

热门文章

  1. 电信主机托管费用_主机托管平均费用与托管流程
  2. ALSA 中 hw 和 plughw 的区别
  3. 【大数据】城市公交网络分析与可视化(四):绘制城市公交(地铁)线路图
  4. 使用ESP8266和MPU6050制作倾斜角度监控器
  5. 网络规划师学习-二层交换机工作原理和二层交换机为啥不能跨网段通信
  6. ad20如何画出pcb板大小_如何用AD更改PCB板子尺寸
  7. jupyter notebook中Nbextensions插件功能大全
  8. vc语言c1083错误,vc++常见错误之二:“fatal error C1083: ”无法打开包括文件-Go语言中文社区...
  9. 郑中基 英雄寞 08版射雕英雄传片头曲 LRC歌词
  10. BZOJ 3097: Hash Killer I