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 </p>
<ul>
<li>必须用 <code>|</code> 管道操作符组合,不能写 <code>std::views::enumerate(v)</code>(C++23 要求适配器对象需通过管道调用)</li>
<li>
<code>[i, x]</code> 中 <code>i</code> 类型是 <code>std::size_t</code>,<code>x</code> 类型是原视图元素的引用(如 <code>int&</code>),不是值拷贝</li>
<li>若原视图是临时对象(如 <code>std::vector{1,2,3} | std::views::enumerate</code>),绑定后使用 <code>x</code> 会导致悬垂引用 —— 这是最容易踩的坑</li>
</ul>
<h3>不能用于哪些容器或表达式</h3>
<p><code>std::views::enumerate</code> 要求输入视图满足 <code>random_access_range</code> 且有 <code>size()</code>,所以以下情况会编译失败:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<ul>
<li>纯输入范围(如 <code>std::istream_view<int></int></code>)—— 没有随机访问和 size</li>
<li>某些自定义 range(未实现 <code>size()</code> 或不满足 <code>random_access_range</code> 概念)</li>
<li>
<code>std::views::filter</code> 后接 <code>enumerate</code>:因为 <code>filter_view</code> 不是 <code>random_access_range</code>,即使底层是 vector 也不行</li>
</ul>
<p>错误示例:</p>
<pre class="brush:php;toolbar:false;">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<:tuple>></:tuple>的地方——它返回的是 view,不是容器;要转存必须显式构造:std::vector result{v | std::views::enumerate}
真正麻烦的从来不是怎么写,而是忘记它返回的是带引用的 tuple 视图——一旦原 range 生命周期结束,绑定的变量就失效。这点比手写 for 循环更隐蔽。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










