折叠表达式是C++引入的一个编译期计算特性,它允许在编译时对参数包进行展开和计算。这意味着所有的折叠表达式运算都是在程序编译阶段完成的,而不是在运行时进行计算,从而可以提供更好的性能。
语法
折叠表达式有下面四种语法。其中 op 代表操作符号,pack 代表传入的变量或者类型
// pack 表示为 (a1,a2,a3)
// 一元左折叠:从左到右计算
(... op pack) // ((a1 op a2) op a3)
// 一元右折叠:从右到左计算
(pack op ...) // (a1 op (a2 op a3))
// 二元左折叠:使用初始值
(init op ... op pack) // (((init op a1) op a2) op a3)
// 二元右折叠:使用初始值
(pack op ... op init) // (a1 op (a2 op (a3 op init)))
下面是上面四种语法的例子
// 1. 一元左折叠示例:从左到右计算所有数字的和
template<typename... Args>
auto sum_left(Args... args) {
return (... + args); // ((1 + 2) + 3) + 4
}
// 使用: sum_left(1, 2, 3, 4) 结果为 10
// 2. 一元右折叠示例:从右到左连接字符串
template<typename... Args>
std::string concat_right(Args... args) {
return (args + ...); // "a" + ("b" + ("c" + "d"))
}
// 使用: concat_right("a", "b", "c", "d") 结果为 "abcd"
// 3. 二元左折叠示例:带初始值的乘法
template<typename... Args>
auto multiply_left(Args... args) {
return (1 * ... * args); // ((1 * 2) * 3) * 4
}
// 使用: multiply_left(2, 3, 4) 结果为 24
// 4. 二元右折叠示例:带初始值的除法
template<typename... Args>
auto divide_right(Args... args) {
return (args / ... / 2.0); // 16 / (8 / (4 / 2.0))
}
// 使用: divide_right(16, 8, 4) 结果为 4.0
当 pack 是 空 时的处理规则
- op 是
&&:求值为true - op 是
||:求值为false - op 是
,:求值为void() - 其他操作符:未定义行为
使用
可变参数模板
允许模板接受任意数量的参数
template<typename... Args> // Args 是一个模板参数包
auto sum(Args... args) { // args 是一个函数参数包
return (... + args); // 一元左折叠
}
多重继承
允许模板接受任意数量的基类
// 多重继承示例
template<typename... Bases>
class Composite : public Bases... {
public:
using Bases::operator()...;
};
#include <iostream>
// 使用示例
int main() {
struct Adder {
int operator()(int a, int b) const { return a + b; }
};
struct Multiplier {
int operator()(float a, float b) const { return a * b; }
};
using Calculator = Composite<Adder, Multiplier>;
Calculator calc;
// 可以使用所有基类的operator()
std::cout << calc(10, 5) << std::endl; // 使用 Adder: 15
std::cout << calc(10.f, 5.f) << std::endl; // 使用 Multiplier: 50
return 0;
}
非类型模板参数
允许模板接受任意数量的值
// 编译期计算数值和
template<int... Numbers>
struct Sum {
static constexpr int value = (... + Numbers);
};
// 使用示例
static_assert(Sum<1, 2, 3, 4>::value == 10);
// 检查数组是否有序
template<int... Numbers>
struct IsSorted {
static constexpr bool value = ((Numbers <= ...) <= Numbers);
};
// 使用示例
static_assert(IsSorted<1, 2, 3, 4>::value); // true
static_assert(!IsSorted<1, 3, 2, 4>::value); // false
类成员初始化中的省略号
可以用于重复初始化值
struct Point {
int x, y, z;
};
// 所有成员使用相同的值初始化
Point p1{1, ...}; // 等同于 Point p1{1, 1, 1};
// 更复杂的例子
struct Matrix {
int values[3][3];
};
Matrix m{{0, ...}, {1, ...}, {2, ...}}; // 每行用相同的值填充
// 等同于:
// Matrix m{{0, 0, 0}, {1, 1, 1}, {2, 2, 2}};
// 也可以用于数组
int arr[5]{42, ...}; // 等同于 int arr[5]{42, 42, 42, 42, 42};