目录
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) = -4cout << 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. 后记
到这里四种折叠表达式就介绍完毕了,每种都给了讲解和代码示例。有不懂的地方欢迎评论区提问