std::ranges::views::enumerate 返回 zip_view,将原视图元素与索引打包为 tuple,支持结构化绑定且 x 为引用,但要求输入为 random_access_range 并有 size(),不可用于 filter_view 等非随机访问视图。

std::ranges::views::enumerate 是什么,能做什么
它不是用来“给容器编号”的语法糖,而是返回一个 std::ranges::zip_view,把原视图的每个元素和对应索引(从 0 开始的 std::size_t)打包成 std::tuple<:size_t t></:size_t>。这意味着你拿到的是“可迭代的索引-元素对”,不是新容器,也不拷贝数据。
常见误用是以为它像 Python 的 enumerate() 那样直接生成 (i, x) 元组并支持结构化绑定——它确实支持,但必须注意引用语义和生命周期。
基本用法与结构化绑定写法
最典型场景:遍历 vector 或 string 并同时访问下标和值。关键点在于结构化绑定时,第二个成员是引用类型,修改它会直接影响原容器:
#include <ranges>
#include <vector>
#include <iostream>
<p>int main() {
std::vector v = {10, 20, 30};
for (auto [i, x] : v | std::views::enumerate) {
std::cout << "index=" << i << ", value=" << x << "\n";
x *= 2; // ✅ 合法:x 是 int&,修改生效
}
// v 现在是 {20, 40, 60}
}- 必须用
|管道操作符组合,不能写std::views::enumerate(v)(C++23 要求适配器对象需通过管道调用) -
[i, x]中i类型是std::size_t,x类型是原视图元素的引用(如int&),不是值拷贝 - 若原视图是临时对象(如
std::vector{1,2,3} | std::views::enumerate),绑定后使用x会导致悬垂引用 —— 这是最容易踩的坑
不能用于哪些容器或表达式
std::views::enumerate 要求输入视图满足 random_access_range 且有 size(),所以以下情况会编译失败:
立即学习“C++免费学习笔记(深入)”;
- 纯输入范围(如
std::istream_view<int>)—— 没有随机访问和 size - 某些自定义 range(未实现
size()或不满足random_access_range概念) -
std::views::filter后接enumerate:因为filter_view不是random_access_range,即使底层是 vector 也不行
错误示例:
auto v = std::vector{1,2,3,4,5};
auto filtered = v | std::views::filter([](int x){ return x % 2; });
for (auto [i,x] : filtered | std::views::enumerate) { /* ❌ 编译失败 */ }解决办法:先转成 vector 再 enumerate,或改用传统 for 循环 + std::ranges::distance 手动计数。
性能和兼容性注意事项
它本身零开销:不分配内存,不拷贝元素,只在迭代时计算索引(本质是 i++)。但要注意:
- C++23 标准才正式纳入,GCC 13+、Clang 16+、MSVC 19.35+ 支持;旧版本需禁用或回退
- 若原 range 的迭代器是昂贵类型(如某些 proxy 迭代器),
enumerate不会额外增加成本,但结构化绑定中取x仍走原迭代器解引用逻辑 - 不能用在需要
std::vector<std::tuple<...>>的地方——它返回的是 view,不是容器;要转存必须显式构造:std::vector result{v | std::views::enumerate}
真正麻烦的从来不是怎么写,而是忘记它返回的是带引用的 tuple 视图——一旦原 range 生命周期结束,绑定的变量就失效。这点比手写 for 循环更隐蔽。


















