c++oding="utf-8" ?>
std::views::keys 和 std::views::values 支持 std::unordered_map,因其 value_type 为 std::pair,满足结构化绑定要求;但 msvc 19.3x 等旧实现有缺陷,建议用 gcc 12+/clang 测试;返回 view 为只读且依赖原容器生命周期。

std::views::keys 和 std::views::values 只适用于关联容器,不支持 unordered_map?
不是不支持,而是 C++20 标准里 std::views::keys 和 std::views::values 要求底层范围的元素类型必须是“可解构为 std::pair<const k v></const>”且支持结构化绑定 —— std::unordered_map 满足这点,但 MSVC 早期(如 19.3x)和某些 libstdc++ 版本存在实现缺陷,导致编译失败。
实操建议:
- 用
clang++ -std=c++20或 GCC 12+ 测试时,std::unordered_map可直接配合std::views::keys使用; - MSVC 用户若遇
error C2672: 'operator |': no matching overloaded function found,可改用std::ranges::transform_view手动提取; - 别依赖容器是否“有序”,而要看其 value_type 是否为
std::pair——std::map、std::multimap、std::unordered_map都行,std::flat_map(C++23)也行。
用 views::keys 提取键后,得到的是 const 引用,不能修改原 map?
对。返回的 view 是只读的:它遍历的是原容器中每个 std::pair 的 first 成员,而该成员在关联容器中是 const K 类型,无法通过 view 修改。
常见错误现象:
- 写
for (auto& k : my_map | std::views::keys) { k = 42; }→ 编译失败,报错类似assignment of read-only reference; - 想“筛选键再批量删”,不能靠
views::keys+erase,因为 view 不持有迭代器所有权,也无法反向定位到原容器节点; - 真正需要修改键时,只能重建容器或用
extract()(C++17 起)逐个移动节点。
views::values 返回的 view 不稳定,插入新元素后可能失效?
没错。view 本身不拷贝数据,只是按需投影原容器的 second 字段 —— 它不保证迭代器稳定性,也不缓存任何状态。
使用场景与风险:
- 适合一次性读取:比如日志打印、统计聚合、转成
std::vector做后续计算; - 禁止在循环中边遍历
views::values边调用my_map.insert(...)—— 迭代器可能被 invalidate,行为未定义; - 若需安全遍历并修改值(非键),应先保存要改的 key,再单独赋值:
for (const auto& [k, v] : my_map) { if (need_update(k)) my_map[k] = new_value; }; - 性能上,
views::values几乎零开销,但每次begin()/end()都重新绑定原容器迭代器,不比手写循环慢。
为什么不能对 std::map 直接用 views::keys | std::views::filter?
能,但要注意 ADL 和命名空间问题 —— 最常见的失败不是语法错,而是没引入 std::views。
典型错误:
- 漏写
using namespace std::views;或没用std::views::filter全名,导致编译器找不到重载; -
std::views::keys返回的是keys_view,它满足std::ranges::input_range,但某些 filter 条件若捕获了非const引用(比如用了[&]且内部修改了外部变量),可能触发隐式 const 限制; - 示例正确写法:
auto ks = my_map | std::views::keys | std::views::filter([](int k) { return k > 10; });; - 如果
my_map是空的,ks仍是合法 range,std::ranges::size(ks)在 C++23 前不可用(无 size() 成员),要用std::ranges::distance。
最易被忽略的一点:views::keys/values 返回的 view 是 lazy 的,不触发任何拷贝或分配,但它的生命周期完全依赖原容器 —— 把 view 存成成员变量却提前销毁了 map,之后访问就是悬垂引用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











