std::ranges::equal_range是c++20提供的在已排序范围中查找目标值等价区间的算法,返回包含first(首个不小于目标的迭代器)和second(首个大于目标的迭代器)的pair,适用于统计频次、批量修改或构造子视图等场景,要求输入范围严格有序。

std::ranges::equal_range 是什么,适合在哪些场景用
std::ranges::equal_range 是 C++20 引入的范围算法,用于在**已排序的范围**中查找某个值的所有出现位置,返回一个 std::pair,其中 first 指向第一个不小于目标值的迭代器,second 指向第一个大于目标值的迭代器 —— 也就是该值的“等价区间”。它本质是 std::lower_bound 和 std::upper_bound 的组合调用,但更简洁、更符合范围习惯。
典型适用场景包括:
- 统计某值在
std::vector或std::array中的出现次数(std::distance(r.first, r.second)) - 批量修改或遍历所有匹配元素(比如把所有
5替换为0) - 配合
std::ranges::subrange构造视图进行后续处理
必须满足的前提条件,否则行为未定义
和所有基于二分搜索的算法一样,std::ranges::equal_range 要求输入范围**严格有序**(默认按 比较),且目标值类型与元素类型可比较。如果范围无序,结果不可预测,不会报错,但大概率找不到或返回错误区间。
- 常见误用:对
std::vector{3,1,4,1,5}直接调用 —— 结果无效 - 正确做法:先确保已排序,或使用
std::ranges::sort(v) - 自定义比较器时,必须与排序所用的谓词完全一致,否则逻辑断裂(例如排序用
std::greater{},搜索却用默认)
基本用法与参数差异
函数签名是:std::ranges::equal_range(range, value, comp = {})。注意它不接受迭代器对,而是直接传整个范围(支持容器、数组、视图等)。
-
range:可以是std::vector、std::array、std::span,甚至std::ranges::iota_view等可范围化对象 -
value:要查找的目标值,类型需能参与比较(不要求与元素类型完全相同,但必须可隐式转换或有对应重载) -
comp:可选比较器,默认是std::less{};若用std::greater{}排序,则此处也必须传std::greater{}
示例:
std::vector<int> v = {1,2,2,2,3,4,4,5};
auto [first, last] = std::ranges::equal_range(v, 2); // first→v[1], last→v[4]
std::cout
<h3>容易踩的坑:迭代器失效与 const 限定</h3>
<p>返回的迭代器类型取决于输入范围的“可写性”。如果传入的是 <code>const std::vector&</code>,则返回 <code>const_iterator</code>;若想修改匹配元素,必须确保原始容器非常量,且范围本身支持可变访问。</p>
<ul>
<li>错误写法:<code>const auto& cv = v; auto r = std::ranges::equal_range(cv, 2); *r.first = 0;</code> → 编译失败(不能通过 const_iterator 写)</li>
<li>注意 <code>std::ranges::equal_range</code> 不接受 <code>std::initializer_list</code>(因为其迭代器是 const-only 且不满足 <code>random_access_iterator</code> 要求)</li>
<li>在 <code>std::map</code> 或 <code>std::set</code> 上慎用:虽然它们有序,但 <code>equal_range</code> 对 key 唯一的容器意义不大(最多返回长度为 0 或 1 的区间);更适合用 <code>find</code> 或 <code>contains</code>
</li>
</ul>
<p>最常被忽略的一点:它不检查范围是否为空 —— 传空容器是安全的,会返回两个相等的迭代器(<code>first == last</code>),但如果你忘了判空就直接解引用,就会 UB。</p></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











