std::ranges::views::values 可用于 std::map 等键值对容器,但需 C++20 支持、包含 头文件并使用管道语法;它返回懒求值视图,不拷贝数据,但依赖原容器生命周期。

std::ranges::views::values 只能用于键值对容器,不能直接用在 std::map
它不是“对 std::map 调用就能遍历 value”的万能视图。std::ranges::views::values 要求其适配的范围中每个元素是可解构为 first 和 second 成员的类型(比如 std::pair),而 std::map 的迭代器解引用后确实返回 std::pair<const key t>&</const> —— 所以它**能用**,但必须注意模板约束和 C++20 特性启用。
常见错误现象:std::ranges::views::values 在 GCC 10 或 Clang 12 以下编译失败,报错类似 no matching function for call to 'values',本质是缺少 <ranges></ranges> 头文件或未开启 C++20 模式。
- 必须包含
#include <ranges></ranges>(不是<algorithm></algorithm>或<map></map>) - 编译需加
-std=c++20(MSVC 对应 `/std:c++20`) -
std::map、std::unordered_map、std::multimap均满足要求;std::vector<:pair int>></:pair>也行
正确写法:从 map 构造视图并遍历 value
不要试图对 map 直接调用 values() 成员函数 —— 它没有这个成员。必须通过 std::ranges::views::values 适配器构造视图:
#include <map>
#include <ranges>
#include <iostream>
int main() {
std::map<int, std::string> m = {{1, "a"}, {2, "b"}, {3, "c"}};
// ✅ 正确:用 views::values 作用于 map 对象
for (const auto& v : m | std::ranges::views::values) {
std::cout << v << "\n"; // 输出 "a", "b", "c"
}
}
关键点:
立即学习“C++免费学习笔记(深入)”;
- 操作符
|是管道语法,来自std::ranges,不是自定义重载 -
v类型是const std::string&(非拷贝),避免不必要复制 - 若需修改 value,用
auto&而非const auto&,但注意std::map的 key 是 const,value 可改
和传统 for 循环或 transform 的性能与语义差异
views::values 是懒求值视图,不拷贝数据,也不分配内存 —— 它只是重新解释迭代器行为。相比手写 for (const auto& p : m) { p.second; },语义等价,但更声明式。
- 零运行时开销:生成的汇编和手写循环几乎一致(GCC/Clang -O2 下)
- 不能用于需要随机访问的场景:视图是 forward_range,不支持
operator[]或data() - 无法直接转成
std::vector:需显式收集,如std::vector{m | std::ranges::views::values} - 兼容性风险:某些嵌入式 STL 实现(如 older libc++)可能未完全实现该视图
容易被忽略的 const 与 lifetime 问题
视图不拥有数据,只持有对原容器的引用。如果原 std::map 在视图使用前被销毁或移动,迭代将导致未定义行为。
- 不要返回局部 map 的
views::values视图(悬垂引用) - lambda 捕获时,若按值捕获 map,则视图绑定的是副本,安全;若按引用捕获,则需确保 map 生命周期长于视图使用
-
std::ranges::views::values不接受右值容器(如std::move(m) | views::values非法),因为视图需维持对左值的引用
真正麻烦的不是怎么写,而是忘了它是个轻量引用 —— 看起来像容器,实际不是。


















