C++20中如何实现带索引的constexpr for循环?
C++20下实现类编译时for循环的最佳方案
问题背景
下面这段代码无法编译:
for (int i = 0; i < 5; ++i) { std::get<i>(tuple); }
原因是i不是编译时常量。
常见的编译时遍历方案(比如遍历std::tuple)多采用递归或std::apply,但这类方法会丢失对索引的直接控制权,而且适用场景也仅限于std::tuple这类特定结构。
当需要实现更灵活的编译时循环逻辑时,比如反向迭代、自定义步长,或是在循环体内使用多个关联索引(如std::get<i>(tuple) * std::get<i + 1>(tuple)),现有方案需要单独设计特殊逻辑,非常繁琐。
提问
在C++20标准下,能实现的最接近constexpr for (int i = 0; i < 5; ++i)的方案是什么?
实现方案
在C++20中,利用编译时整数序列结合模板lambda可以实现接近原生constexpr for的效果,同时保留对索引的完全控制权,且不限于std::tuple场景。
核心思路
借助编译期整数序列生成固定的索引集合,再通过模板lambda遍历每个索引值——由于索引是编译期常量,可直接用于std::get、模板参数等场景。同时可以轻松扩展实现反向迭代、自定义步长等需求。
基础实现(正向循环)
#include <utility> #include <tuple> // 通用编译时循环模板 template <std::size_t Start, std::size_t End, std::size_t Step = 1, typename Func> constexpr void constexpr_for(Func&& func) { if constexpr (Start < End) { func(std::integral_constant<std::size_t, Start>{}); constexpr_for<Start + Step, End, Step>(std::forward<Func>(func)); } } // 使用示例 int main() { auto t = std::make_tuple(1, 2.3, "hello"); // 正向遍历tuple索引0到2 constexpr_for<0, std::tuple_size_v<decltype(t)>>([&](auto i) { // i是编译期常量,可直接用于std::get auto val = std::get<i>(t); // 这里可以执行任意编译期/运行期操作 }); return 0; }
扩展场景实现
- 反向迭代
只需调整模板参数的起始、结束值和步长:
// 反向遍历tuple索引2到0 constexpr_for<std::tuple_size_v<decltype(t)>-1, -1, -1>([&](auto i) { auto val = std::get<i>(t); });
- 自定义步长
比如每隔2个索引遍历:
constexpr_for<0, 10, 2>([&](auto i) { // 处理i=0,2,4,6,8 });
- 多索引关联操作
在lambda中直接使用i+1等编译期表达式:
constexpr_for<0, std::tuple_size_v<decltype(t)>-1>([&](auto i) { auto product = std::get<i>(t) * std::get<i+1>(t); });
优势
- 保留对索引的直接控制权,可在循环体内自由使用编译期索引值
- 支持自定义起始、结束、步长,适配各种循环需求
- 不局限于
std::tuple,可用于数组、模板参数包等任何需要编译期索引的场景
内容的提问来源于stack exchange,提问作者Stack Danny
相关产品推荐
相关产品推荐

