折叠表达式(fold expression)是C++17引入的一项强大功能,它让我们可以在不需要手动递归展开参数包的情况下,对所有参数包元素执行相同的二元运算。通过这种方式,模板元编程中的“可变参数包装器”变得更加简洁、易读,也大大降低了代码出错的概率。下面将从语法、典型使用场景以及实战案例三个方面进行讲解。
一、折叠表达式语法
折叠表达式分为两类:左折叠和右折叠,根据括号的位置决定运算顺序。语法结构为:
(... op pack) // 左折叠
(pack op ...) // 右折叠
其中 op 是任意二元运算符(如 +, *, &&, || 等),pack 是参数包(Args...)。如果想把参数包先转成一个列表,再做折叠,也可以使用括号包裹:
(... op (pack))
(pack op (...))
注意:折叠表达式只能作用于可变参数模板,不能直接作用于普通函数参数。
二、典型使用场景
-
变参函数的求和 / 乘积
直接用(... + args)或(... * args),避免显式循环。 -
可变参数对象的构造
通过折叠调用构造函数或成员函数,例如std::initializer_list或std::tuple的构造。 -
日志系统的可变参数输出
把所有日志参数用operator<<连续输出,写成折叠表达式。 -
元编程的逻辑判断
使用(... && std::is_same_v<T, U>...)判断参数包中所有类型是否相同。
三、实战案例:实现可变参数加法器
下面给出一个可变参数加法器的完整实现,并演示其使用。
#include <iostream>
#include <vector>
#include <numeric>
#include <type_traits>
// 1. 基础加法器
template<typename... Args>
auto variadic_sum(Args&&... args) {
return (std::forward <Args>(args) + ...);
}
// 2. 带类型检查的加法器(只接受整数类型)
template<typename... Args>
auto strict_variadic_sum(Args&&... args) {
static_assert((std::is_integral_v<std::decay_t<Args>> && ...),
"所有参数必须是整数类型");
return (std::forward <Args>(args) + ...);
}
// 3. 将参数包转成 std::vector 并求和
template<typename... Args>
auto sum_to_vector(Args&&... args) {
std::vector <int> vec{std::forward<Args>(args)...};
return std::accumulate(vec.begin(), vec.end(), 0);
}
int main() {
std::cout << "variadic_sum(1, 2, 3) = " << variadic_sum(1, 2, 3) << '\n';
std::cout << "strict_variadic_sum(10, 20) = " << strict_variadic_sum(10, 20) << '\n';
// static_assert 触发的错误演示(取消注释可见效果)
// std::cout << strict_variadic_sum(1, 2.5, 3); // error
std::cout << "sum_to_vector(4, 5, 6) = " << sum_to_vector(4, 5, 6) << '\n';
}
输出
variadic_sum(1, 2, 3) = 6
strict_variadic_sum(10, 20) = 30
sum_to_vector(4, 5, 6) = 15
在
strict_variadic_sum中,static_assert((std::is_integral_v<std::decay_t<Args>> && ...), "...")就是一个折叠表达式,用来保证所有参数都是整数。
四、折叠表达式与 SFINAE
折叠表达式可以与 SFINAE(Substitution Failure Is Not An Error)无缝结合,实现更细粒度的函数重载。例如:
template<typename... Args,
std::enable_if_t<(std::conjunction_v<std::is_arithmetic<Args>...>), int> = 0>
auto arithmetic_sum(Args&&... args) {
return (std::forward <Args>(args) + ...);
}
这里 std::conjunction_v 也是一个折叠表达式,用来检测所有类型是否都是算术类型。
五、总结
折叠表达式在 C++17 中极大简化了可变参数模板的实现。它既能提升代码可读性,又能避免传统递归展开带来的模板爆炸风险。无论是实现通用加法、乘积,还是构造可变参数容器,折叠表达式都是不可或缺的工具。建议在项目中合理使用,既能写出简洁高效的代码,又能提升团队编码规范的统一性。