折叠表达式
(
F
o
l
d
E
x
p
r
e
s
s
i
o
n
s
)
(Fold \ Expressions)
(Fold Expressions)是
C
+
+
17
C++17
C++17标准引入的,引入折叠表达式的主要目的是计算某个值(表达式的结果当然是一个值)。这个值的特殊在于:
它与所有可变参有关,而不是与但单独某个可变参有关。
由于可变参的展开比较繁琐,所以使用折叠表达式,可以不展开参数而计算出可变参的结果。
一、一元左折 ( U n a r y L e f t F o l d ) (Unary\ Left\ Fold) (Unary Left Fold)
格式:(…运算符 一包参数)
计算方式:((参数1 运算符 参数2)运算符 参数3…)
一元左折的计算是从最左边开始的,不断向左折叠。
注意的是,折叠表达式都需要加上括号,以下不在赘述。
下面是一元左折的简单使用:
//一元左折
template<typename... T>
int add_left(T... args) {
return (... + args); //不能省略括号
}
template<typename... T>
int del_left(T...args) {
return (... - args);
}
调用下面的函数
void Test1() {
std::cout << add_left(10, 20, 30) << "\n";
std::cout << del_left(10, 20, 30) << "\n";
}
得到结果为:
可以验证发现运算顺序如上文描述一样。
二、一元右折 ( U n a r y R i g h t F o l d ) (Unary\ Right\ Fold) (Unary Right Fold)
格式:(一包参数 运算符…)
计算方式:(参数1 运算符(…(参数n-1 运算符 参数n)) )
一元右值的计算是从最右边开始的,不断向右折叠。
下面是一元右折的使用:
template<typename... T>
int del_left(T...args) {
return (... - args);
}
void Test1() {
std::cout << add_left(10, 20, 30) << "\n";
std::cout << del_left(10, 20, 30) << "\n";
}
//一元右折
template<typename ...T>
int del_right(T...args) {
return (args - ...); //注意参数中...的位置
}
void Test2() {
std::cout << del_right(10, 20, 30) << "\n";
}
运行后,得到:
这个运算顺序,显然是:
(
10
−
(
20
−
30
)
)
=
10
−
(
−
10
)
=
20
(10-(20-30)) = 10 - (-10) = 20
(10−(20−30))=10−(−10)=20
符合一元右折的计算规律。
三、二元左折 ( B i n a r y L e f t F o l d ) (Binary\ Left\ Fold) (Binary Left Fold)
二元左折和一元左折的区别就是,二元左折可以传入自定义的 i n i t init init初始值参与计算。
格式:(
i
n
i
t
init
init 运算符 … 运算符 一包参数)
计算方式:(((
i
n
i
t
init
init 运算符 参数1)运算符 参数2 )… 运算符 参数N)
二元左折的使用如下:
这里,二元左折还能使用一个支持运算符的对象,比如这里使用的是
c
o
u
t
cout
cout对象:
//二元左折
template<typename... T>
int del_left_b(T...args) {
return (100 - ... - args); //参数为数值
}
template<typename... T>
void print(T...args) {
(std::cout << ... << args); //参数为cout
}
void Test3() {
std::cout << del_left_b(10, 20, 30) << "\n";
print(1, "abc", 1.2, 'x');
}
调用结果如下:
四、二元右折 ( B i n a r y R i g h t F o l d ) (Binary\ Right\ Fold) (Binary Right Fold)
二元右折和二元左折类似,在格式和计算方式上不同:
格式:(一包参数 运算符 … 运算符 init)
计算方式(参数1 运算符(…(参数N 运算符 init)))
下面是使用二元右折的例子:
//二元右折
template<typename... T>
int del_right_b(T...args) {
return (args - ... - 100);
}
void Test4() {
std::cout << del_right_b(10, 20, 30) << "\n";
}
结果如下:
五、可变参表达式
当然,如果我们想对每个传入的参数进行 ∗ 2 *2 ∗2操作,然后再累加后输出每一项的结果呢,这时我们就需要写一个可变参表达式,使用折叠表达式来计算最终的结果,还需要一个中间模板来传递 2 ∗ a r g s 2*args 2∗args的信息。
模板如下:
//可变参表达式
template<typename... T>
auto print_result(T const&... args) {
(std::cout << ... << args) << "结束\n"; //注意括号是必要的
return (... + args); //一元左折
}
template<typename...T>
void print_calc(T const&... args) {
std::cout << print_result(2 * args...) << "\n";
}
调用下面代码:
void Test5() {
print_result(1, 2, 3, 4, 5);
print_calc(1, 2, 3, 4, 5);
}
运行结果为:
我们成功得到了每个数 ∗ 2 *2 ∗2的单独的结果,并且得到了累加后的总和。
注意下面几个写法:
//错误
print_result(args *2);
print_result(args...*2);
print_result(args * 2...);
print_result(2* args);
//正确
print_result(args * 2 ...);
print_result((args * 2) ...);
print_result(2 * args ...);
print_result((2 * args) ...);
print_result((args+args) ...);