一、解决什么问题?
你有一个 tuple,想调用一个函数,参数就是这个 tuple 里的元素:
auto t = std::make_tuple(1, 2.0, "hello");
// 我想调用:func(1, 2.0, "hello")
// 但怎么写?不能写 func(t[0], t[1], t[2]) 因为 tuple 不能用 [] 下标
index_sequence 的答案:编译期生成 0, 1, 2, ..., N-1 这个序列,然后用包展开去 get tuple 的元素。
二、std::apply 的底层实现(搞懂这个就懂了 index_sequence)
// 1. 先生成 index_sequence<0, 1, 2>
// 2. 用 (get<I>(t))... 展开成 get<0>(t), get<1>(t), get<2>(t)
template <typename Func, typename Tuple, size_t... I>
auto apply_impl(Func&& f, Tuple&& t, std::index_sequence<I...>) {
// I... = 0, 1, 2
// get<I>(t)... = get<0>(t), get<1>(t), get<2>(t)
return f(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename Func, typename Tuple>
auto my_apply(Func&& f, Tuple&& t) {
// tuple_size_v<Tuple> 得到元素个数,比如 3
// make_index_sequence<3> → index_sequence<0, 1, 2>
return apply_impl(
std::forward<Func>(f),
std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{}
);
}
my_apply([](int a, double b, const char* c) {
std::cout << a << b << c;
}, t);
// 展开为:apply_impl(lambda, t, index_sequence<0, 1, 2>)
// → lambda(get<0>(t), get<1>(t), get<2>(t))
// → lambda(1, 2.0, "hello")
什么时候你会自己写这个?
- 不常见。但你用
std::apply的时候应该知道底层怎么工作的。 - 面试官可能让你手写
apply,或者问"tuple 的元素怎么展开成函数参数"。
三、make_index_sequence 底层原理(面试加分项)
// 编译期生成 index_sequence<0, 1, 2, ..., N-1>
// 递归模板实现:
// 前置声明
template <size_t N, size_t... I>
struct make_index_sequence_impl
: make_index_sequence_impl<N - 1, N - 1, I...> {};
// 每递归一次,在 I... 前面插入一个数
// 终止条件:N = 0
template <size_t... I>
struct make_index_sequence_impl<0, I...> {
using type = std::index_sequence<I...>; // 最终生成序列
};
template <size_t N>
using make_index_sequence = typename make_index_sequence_impl<N>::type;
// make_index_sequence<3> 的编译期展开过程:
// make_index_sequence_impl<3> → make_index_sequence_impl<2, 2>
// → make_index_sequence_impl<1, 1, 2>
// → make_index_sequence_impl<0, 0, 1, 2>
// → index_sequence<0, 1, 2> ✅