std::ranges::views::keys 仅适用于满足 tuple-like 要求的关联容器(如 std::map),不保证兼容 std::unordered_map;必须用管道操作符 | 使用,返回 const K& 视图,零成本抽象但需注意生命周期和只读约束。

std::ranges::views::keys 只能用于关联容器,不能用于 std::unordered_map
它要求底层视图的元素类型是 std::pair<const k v></const> 且支持 std::get(e) —— 这在 std::map、std::set(注意:set 的 pair 是 std::pair<const k void></const>)等有序容器中成立,但 std::unordered_map 的迭代器解引用结果虽也是 std::pair<const k v></const>,其内部实现却可能不满足 std::ranges::viewable_range + std::tuple_size_v 约束(尤其在较旧标准库如 libstdc++ 12 前),导致编译失败或未定义行为。
常见错误现象:
-
error: no matching function for call to 'keys'(GCC 12.2 或更低) static_assert failed: "views::keys requires a range of tuple-like types"
实操建议:
- 用
std::map或std::multimap替代std::unordered_map,若仅需键遍历且顺序无关,改用传统循环:for (const auto& [k, v] : umap) { /* use k */ } - C++23 起部分标准库(如 MSVC 19.35+、libstdc++ 13+)已放宽限制,但仍建议优先验证
std::ranges::views::keys(umap)是否可编译
必须配合 range adaptor pipe operator 使用,不能直接构造
std::ranges::views::keys 是一个 range adaptor object,不是函数模板也不是视图类型,不能像 std::views::filter 那样传参调用;它只能通过管道操作符 | 应用于一个适配的 range。
立即学习“C++免费学习笔记(深入)”;
错误写法:
auto keys_view = std::ranges::views::keys(my_map); // ❌ 编译失败
正确写法:
auto keys_view = my_map | std::ranges::views::keys; // ✅
使用场景:
- 链式组合时必须保持左结合:
my_map | std::views::keys | std::views::take(3) - 若想提前获取键视图并复用,需用
auto推导(不可用std::ranges::keys_view,该类型未公开) - 不能对临时 map 写
std::map<int char>{} | std::views::keys</int>,因为临时对象生命周期不足以支撑视图迭代
遍历时 key 类型是 const 引用,修改 key 会触发未定义行为
std::ranges::views::keys 返回的每个元素是底层 std::pair 中 first 成员的 const& —— 即 const K&。试图通过它修改 key(比如 *it = new_key)既不可行,也不合法。
常见错误现象:
error: assignment of read-only location- 即使绕过编译(如 const_cast),也会破坏 map 内部红黑树结构,后续查找/插入崩溃
实操建议:
- 只读遍历:直接用
for (const auto& k : map | std::views::keys) { ... } - 需要替换 key?必须先
erase原键值对,再insert新的{new_key, value} - 若需同时访问 key 和 value,别拆成两步,直接用结构化绑定:
for (const auto& [k, v] : map) { ... }
性能无额外开销,但底层仍依赖 map 迭代器遍历
std::views::keys 是零成本抽象:它不拷贝键,不分配内存,只是重定向迭代器的 operator* 返回 it->first。时间复杂度与原容器迭代完全一致(O(n),每次 ++ 是摊还常数)。
但要注意:
- 它不改变底层容器的遍历顺序 ——
std::map仍是升序,std::multimap按 key 排序后按插入顺序稳定 - 如果 map 很大且只取前几个键,配合
std::views::take可提前终止,但std::views::keys本身不提供随机访问,无法keys_view[5] - 调试时观察
keys_view类型会看到类似std::ranges::ref_view<:map>></:map>,说明它只是包装而非复制
真正容易被忽略的是:你得确保 map 本身没被移动或析构 —— 视图不拥有数据,悬空引用比裸指针更隐蔽。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/shoujipingce/123969.html