c++oding="utf-8" ?>
std::ranges::views::values 在 std::map 上可用但需 c++20+、显式限定或 adl 支持,要求标准库支持(如 libstdc++13+),提取 value 为引用,可修改原 map;替代方案 views::transform 更兼容。

std::ranges::views::values 在 map 上根本不能直接用
它只对“键值对序列”有效,而 std::map 的迭代器解引用后是 std::pair<const key value></const>,看起来像键值对,但 std::ranges::views::values 要求容器元素本身是可解构为 .first 和 .second 的类型——这点满足;但它真正卡在:该 view 要求底层 range 的 value_type 必须是 *cv-qualified* std::pair(或等效结构),而 std::map 的 value_type 确实是 std::pair<const key value></const>,所以语法上其实合法。问题出在实际编译器支持和常见误用上。
多数人写错是因为没注意 ADL 或命名空间,或者误以为它像 Python 的 .values() 那样“开箱即用”。真实情况是:它能用,但必须显式引入、且仅适用于 C++20 及以上 + 支持 std::ranges 的标准库(如 libstdc++ 13+、libc++ 15+、MSVC 19.3x+)。
- 必须
#include <ranges></ranges>和#include <map></map> - 不能直接写
my_map | std::ranges::views::values—— 缺少 ADL 支持,得用std::ranges::views::values完整限定,或启用using namespace std::ranges::views; -
std::unordered_map同样适用,但std::vector<:pair>></:pair>更稳妥(无 const key 限制)
正确写法:带 const key 的 map 也能用,但要注意推导类型
std::map<int std::string></int> 的 value_type 是 std::pair<const int std::string></const>,views::values 会提取 .second,返回类型是 std::string&(非 const 引用),所以修改 map 中的 value 是允许的;但如果 map 是 const 限定(比如函数参数为 const std::map<...>&</...>),则得到的是 const std::string&。
示例:
#include <map>
#include <ranges>
#include <vector>
#include <iostream><p>int main() {
std::map<int std::string> m = {{1,"a"}, {2,"b"}, {3,"c"}};</int></p>
<pre class="brush:php;toolbar:false;">// ✅ 正确:显式限定,或 using 前置
auto vals = m | std::ranges::views::values;
// vals 是 view 类型,遍历时拿到的是 std::string&
for (std::string& s : vals) {
s += "!"; // 修改原 map 中的值
}
// m 现在是 {{1,"a!"}, {2,"b!"}, {3,"c!"}}
}
替代方案:为什么有人用 transform + pair::second 更可靠
不是所有项目都能用最新标准库,尤其嵌入式或旧 GCC 环境下 views::values 可能未完全实现或触发 internal compiler error。此时手动 transform 更可控:
- 兼容 C++17(配合
std::make_transform_iterator)或 C++20(用views::transform) - 避免依赖
std::ranges::views::values对pair成员访问的隐式约定 - 类型明确:
views::transform([](const auto& p) { return p.second; })
对比:
// 替代写法(更泛用)
auto vals_alt = m | std::ranges::views::transform([](const auto& p) {
return p.second; // 显式取 second,不依赖 views::values 内部逻辑
});
这个写法在 clang 14、GCC 11.2+、MSVC 19.30+ 都稳定工作,且更容易调试——比如加日志、条件过滤。
常见编译错误和坑
遇到 error: no match for 'operator|' 或 concept check failed 时,大概率不是语法错,而是环境或依赖问题:
-
std::ranges::views::values不是普通函数,是定制点对象(customization point object),需 ADL 找到适配器;若没using namespace std::ranges::views;且没完整限定,GCC 可能找不到重载 - libstdc++ 在 GCC 12 及以前默认禁用
std::ranges的部分特性,需编译时加-D_GLIBCXX_USE_CXX11_ABI=1或升级到 GCC 13+ - Clang 需要
-stdlib=libc++且 libc++ 版本 ≥ 15 - VS2022 17.5+ 支持,但需确认项目设置为 C++20 或更高,且未定义
_HAS_CXX20=0
最简单的验证方式:先用 std::vector<:pair int>></:pair> 测试 views::values 是否可用,再换到 std::map —— 排除容器特化问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











