std::vector<int> v(5); std::iota(v.begin(), v.end(), 0); 可模拟 range(5),但需预分配内存,非惰性,适用于小范围场景。

用 std::iota + std::vector 模拟最简 range
Python 的 range(5) 本质是惰性生成整数序列,C++ 没原生语法糖,但最直白的替代是预分配一个 std::vector 并用 std::iota 填充。适合小范围、可接受内存开销的场景。
常见错误:直接写 for (int i = 0; i —— 这不是 range 遍历,只是传统循环;想复刻 Python 的“可迭代对象”语义,就得构造容器或视图。
-
std::vector大小必须提前知道,n不能是运行时才确定的超大值(比如1e9),否则 OOM -
std::iota要求起始迭代器可写,不能对std::array或字面量使用 - 示例:
std::vector<int> r(n);<br>std::iota(r.begin(), r.end(), 0);<br>for (int x : r) { /* ... */ }
用 C++20 std::views::iota 实现真正惰性 range
这才是最接近 Python range 的方案:不分配内存、支持任意整型(包括 long long)、可组合、延迟计算。但必须开启 C++20 且链接标准库的 ranges 支持。
常见错误:编译报错 ‘views’ is not a member of ‘std’ —— 忘了加 #include <ranges>,或编译器没开 -std=c++20;还可能误用 std::ranges::iota(那是算法,不是视图)。
立即学习“Python免费学习笔记(深入)”;
- 起始值类型决定整个视图类型,
std::views::iota(0)是int,std::views::iota(0LL)才是long long - 无限视图需手动截断,例如
std::views::iota(0) | std::views::take(n),否则 range-for 会死循环 - 示例:
for (int x : std::views::iota(0) | std::views::take(5)) {<br> // x = 0,1,2,3,4<br>}
自定义 range 类时,别忽略 begin()/end() 的 const 正确性
如果自己写一个 IntRange 类模拟 range,最容易翻车的是迭代器在 const 对象上调用失败——因为 begin() 和 end() 没加 const 限定符。
使用场景:需要跨平台兼容旧编译器,或要控制步长(如 range(0, 10, 2)),而 std::views::iota 只支持步长为 1。
- 必须同时提供 const 和非 const 版本的
begin()/end(),否则for (auto x : my_range)在 const 上下文中编译不过 - 返回类型别名要小心:用
std::iterator_t<Self>而不是硬写int*,否则泛型失效 - 性能上,只要不存储实际数据,自定义类和视图一样是零开销抽象
为什么不用 boost::irange?
Boost 确实有 boost::irange(0, n),行为类似 Python range,但它引入外部依赖,且在 C++20 下属于冗余方案。
容易踩的坑:某些旧版 Boost(如 1.65 之前)的 irange 返回的迭代器不满足 LegacyIterator 要求,在部分 STL 算法中失效;另外它不支持 long long 起始值,会静默截断。
- 除非项目已强依赖 Boost 且无法升级到 C++20,否则没必要用
- 替代方案更轻量:
std::views::iota(0) | std::views::take(n)功能等价,无额外依赖 - 如果真要用,务必检查
BOOST_VERSION≥ 1.70,并显式指定整型模板参数
C++ 的 range 遍历没有“一行语法糖”,核心分歧在于:要不要惰性、支不支持超大范围、能不能跨 C++ 标准版本。选 std::views::iota 是当前最干净的解,但得确认团队编译环境真能跑起来——很多遗留构建系统卡在 C++17,这时候硬上视图反而增加维护成本。









