一、解决什么问题?
std::tuple 的元素类型不同,不能用普通 for 循环遍历。
需求:我想对 tuple 的每个元素调用同一函数。
std::tuple<int, double, std::string> t = {1, 2.0, "hello"};
// 我想实现:
// print(get<0>(t));
// print(get<1>(t));
// print(get<2>(t));
// 但不能写 for (int i = 0; i < 3; i++) print(get<i>(t)); ← 编译 error,i 不是编译期常量
二、if constexpr + 递归——编译期循环
template <typename Tuple, typename Func, size_t I = 0>
void tuple_for_each(const Tuple& t, Func&& f) {
if constexpr (I < std::tuple_size_v<Tuple>) {
f(std::get<I>(t)); // 处理第 I 个元素
tuple_for_each<Tuple, Func, I + 1>(t, // 递归处理下一个
std::forward<Func>(f));
}
// if constexpr 的关键:当 I >= tuple_size 时,
// 这个递归调用分支的代码直接被编译器丢弃,
// 不会产生无限递归!
}
编译器展开:
tuple_for_each(t, f, 0)
if constexpr (0 < 3) → true
f(get<0>(t))
tuple_for_each(t, f, 1)
if constexpr (1 < 3) → true
f(get<1>(t))
tuple_for_each(t, f, 2)
if constexpr (2 < 3) → true
f(get<2>(t))
tuple_for_each(t, f, 3)
if constexpr (3 < 3) → false
// 整个递归调用分支被丢弃!不生成代码
最终生成的代码等价于:
f(get<0>(t));
f(get<1>(t));
f(get<2>(t));
零运行时开销——完全展开为 3 条语句。
三、什么时候需要手写 tuple 遍历?
| 场景 | 示例 |
|---|---|
| 序列化库 | 把 tuple<int, double, string> 按顺序写到文件 |
| GUI 属性绑定 | 遍历元组生成 QLabel 列表 |
| 反射替代 | 遍历结构体成员做批量校验 |
| 策略合并 | tuple 里放了多个策略对象,遍历执行它们 |
四、第五阶段总结——一张表记住
| 技巧 | 解决什么问题 | 替代了什么 | 是否常用 |
|---|---|---|---|
| CRTP | 编译期多态,消除虚函数开销 | 虚函数继承 | 写库时常用 |
| Variadic Templates | 类型安全的任意参数 | va_args/重载 | 天天用 |
| Fold Expressions | 参数包的简化操作 | 递归模板 | C++17 后常用 |
| index_sequence | tuple 展开为参数 | 无 | 面试常考 |
| tuple 遍历 | 异质容器逐元素操作 | 无 | 序列化等场景 |