在 C++20 之前,模板元编程往往需要通过递归实现对参数包的遍历。
从 C++17 开始,折叠表达式(fold expression)被引入,极大简化了这种递归逻辑。
本文将深入解析折叠表达式的语法、工作原理、常见用法,并给出一系列实战案例,帮助你快速掌握并应用于实际项目中。
一、折叠表达式基本语法
折叠表达式可以把一个二元运算符(如 +、&&、||、== 等)折叠成对参数包中所有元素的一次全序或并序运算。
基本形式有三种:
- 左折叠(left fold)
(pack op ...) // 等价于 (... op pack) - 右折叠(right fold)
(... op pack) // 等价于 (pack op ... op pack.back()) - 全折叠(full fold)
(op pack ... op) // 需要包含左、右两侧的运算符
二、典型示例
-
参数包求和
template<typename... Ts> auto sum(Ts... ts) { return (... + ts); // 右折叠 } static_assert(sum(1,2,3,4) == 10); -
参数包布尔与
template<typename... Bools> constexpr bool all_true(Bools... bs) { return (... && bs); // 右折叠 } static_assert(all_true(true, true, true)); -
参数包取最大值
template<typename T, typename... Ts> constexpr T max(T first, Ts... rest) { return (first > ... > rest) ? first : max(rest...); // 递归折叠 } static_assert(max(3, 8, 2, 5) == 8);
三、折叠表达式与类型特性 折叠表达式不仅可以处理值,还可以用于类型列表。例如,实现类型推导:
template<typename... Types>
struct type_list {};
template<typename... Ts>
struct common_type_t {
using type = decltype((std::common_type_t <Ts>..., std::declval<void*>()));
};
四、折叠表达式在C++20 Concepts中的应用 C++20 Concepts 通过约束(requires)来限制模板参数。折叠表达式可以轻松实现多参数约束:
template<typename... Args>
concept all_integral = (std::integral <Args> && ...);
static_assert(all_integral<int, long, short>);
五、性能与编译时间
折叠表达式的编译器实现往往是递归展开,而不是逐个展开。对于大型参数包,编译时间可能显著增加。建议在必要时使用 constexpr 与 inline,或考虑将逻辑拆分成多个小模板。
六、实战:实现一个通用的 apply 函数
template<typename Func, typename... Args>
decltype(auto) apply(Func&& f, Args&&... args) {
return std::invoke(std::forward <Func>(f),
std::forward <Args>(args)...);
}
此处不需要折叠表达式,但如果你想对参数包做预处理,可以使用折叠:
template<typename Func, typename... Args>
decltype(auto) safe_apply(Func&& f, Args&&... args) {
// 先检查所有参数是否满足某些条件
static_assert((std::is_arithmetic_v<std::decay_t<Args>> && ...));
return std::invoke(std::forward <Func>(f),
std::forward <Args>(args)...);
}
七、常见错误与调试技巧
- 参数包为空:折叠表达式要求至少有一个参数,否则会产生语法错误。可以用默认值或特殊重载来处理空包情况。
- 优先级问题:折叠表达式本身是一个完整表达式,需根据运算符优先级添加括号以避免歧义。
- 类型推断失误:折叠表达式返回的类型取决于运算符及参数类型。若不确定可使用
decltype或auto。
八、结语
折叠表达式是 C++20 里最实用且低门槛的元编程工具之一。它不仅简化了代码,还提高了可读性与可维护性。掌握折叠表达式后,你可以在模板元编程中实现更高层次的抽象,并与 Concepts、constexpr 等新特性结合,构建更安全、更高效的 C++ 模块。祝你编码愉快!