std::views::keys 和 std::views::values 可用于 std::map,但必须用管道语法(如 m | std::views::keys),不可直接调用;要求源 range 元素为 pair-like 类型,且视图不拥有数据、依赖原容器生命周期。

直接说结论:std::views::keys 和 std::views::values 不能直接用于 std::map 或 std::unordered_map 的裸对象——它们只接受“范围(range)”,而 std::map 本身是范围,但其元素类型是 std::pair<const k v></const>,视图适配器需要能从中提取 .first 或 .second;C++20 标准库确实提供了这两个视图,但用法有隐含前提,且容易因类型不匹配编译失败。
std::views::keys 要求 range 的 value_type 必须可解构为 pair-like 类型
编译器不会自动把 std::map<int std::string></int> 当作“键值对序列”来处理——它确实是,但 std::views::keys 内部依赖 std::get(v) 或 v.first 访问键,这要求 v(即 range 的每个元素)必须满足 std::is_tuple_like_v 或至少支持 .first 成员访问。而 std::map::value_type 是 std::pair<const key t></const>,满足条件,所以它本应可用——但常见错误是忘了加 | 管道或误传了指针/引用。
正确写法:
auto m = std::map<int std::string>{{1,"a"},{2,"b"}};
auto keys = m | std::views::keys; // ✅ OK:m 是 lvalue range,value_type 是 pair
for (int k : keys) { /* ... */ }</int>
错误写法示例:
-
std::views::keys(m)—— ❌ 编译失败,std::views::keys是一个适配器对象,不是函数 -
auto keys = &m | std::views::keys—— ❌ 指针不是 range,std::ranges::range<decltype></decltype>为 false -
auto keys = std::move(m) | std::views::keys—— ⚠️ 可能失效,因为移动后m处于有效但未指定状态,且某些 view 实现对右值 range 支持不一
std::views::values 对 const map 会推导出 const value_type 引用
当你对 const std::map<k>&</k> 应用 std::views::values,得到的视图中元素类型是 const V&,而非 V& 或 V。这意味着你无法通过该视图修改原 map 中的 value(当然也不应该),但如果后续想用 std::vector 构造或算法写入,得注意 cv 限定符是否匹配。
例如:
const std::map<int std::string> m{{1,"hello"}};
auto vals = m | std::views::values;
// vals 的迭代器 value_type 是 const std::string&
std::vector<:string> v(vals.begin(), vals.end()); // ✅ OK:const string& → string
std::vector<:string_view> sv(vals.begin(), vals.end()); // ❌ 不行:const string& 不能隐式转 string_view(需 .sv() 或 .data())</:string_view></:string></int>
关键点:
- 非 const map →
std::views::values给出V&(可读可写,但写操作会改原 map) - const map → 给出
const V&(只读安全) - 若需拷贝值而非引用,显式用
| std::views::transform([](const auto& v) { return v; })或直接构造容器
与传统 for-loop 或 std::transform 相比,性能几乎无差别,但语义更清晰
生成键或值的只读视图本身是零开销抽象:不分配内存、不复制元素,只是包装迭代器。但要注意,视图是 lazy 的,每次遍历都重新走一遍 map 迭代器路径,和手写 for (const auto& p : m) { use(p.first); } 在汇编层基本一致。
不过有两个实际差异点:
-
std::views::keys(m)写法非法,必须用管道m | std::views::keys,否则编译报错(典型错误信息:no match for 'operator|') - 在模板上下文中,比如函数参数为
std::ranges::input_range auto&& r,传入m | std::views::keys是合法的,但传m本身可能触发不同重载,需留意 SFINAE 行为 - Clang 15+ / GCC 12+ 支持良好,MSVC 2022 17.4+ 基本可用,但早期版本可能缺失
std::views::keys符号(需确认__cpp_lib_ranges宏 >= 201911L)
最易被忽略的一点:这两个视图返回的是 std::ranges::view_interface 派生类型,不是 std::vector 或数组——它们不拥有数据,生命周期完全依赖源 range。一旦原 std::map 被销毁,再访问视图就 UB。别把它当成“键列表快照”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











