std::ranges::equal_range是c++20范围库中用于在已排序随机访问范围内查找目标值等值区间(左闭右开)的算法,返回subrange;适用于vector、array等已排序容器中定位重复元素、统计频次或批量操作,要求范围满足sorted且比较器一致,否则行为未定义。

std::ranges::equal_range 是什么,适合什么场景
std::ranges::equal_range 不是“二分搜索的通用入口”,它是专为已排序范围设计的、返回一对迭代器的算法——左边界(首个不小于给定值的位置)和右边界(首个大于给定值的位置)。它本质是 std::lower_bound 和 std::upper_bound 的组合调用,但只做一次遍历(底层通常仍是两次二分,但接口统一、语义清晰)。
典型使用场景:查找重复元素的连续块,比如在 std::vector<int>{1,2,2,2,3,4}</int> 中找所有 2 的位置区间;或判断某值是否存在且获取其全部出现范围。
注意:它要求范围必须满足 std::ranges::sorted —— 即升序排列(默认比较),否则行为未定义。
怎么写最简可用代码
直接传入范围和待查值即可,无需手动写迭代器对:
std::vector<int> v = {1, 2, 2, 2, 3, 4};
auto [first, last] = std::ranges::equal_range(v, 2); // C++20 结构化绑定
// first 指向第一个 2,last 指向第一个 >2 的元素(即 3)
int count = std::distance(first, last); // 得到重复次数:3</int>
如果编译器不支持结构化绑定,用传统方式:
auto result = std::ranges::equal_range(v, 2); auto first = result.begin(); auto last = result.end(); // 注意:result 是 std::ranges::subrange,不是 pair
关键点:
-
std::ranges::equal_range返回的是std::ranges::subrange<it></it>,不是std::pair<it it></it>(老式std::equal_range才返回 pair) - 所以不能写
auto p = ...; auto first = p.first;,会编译失败 - 要用
.begin()/.end()或结构化绑定(推荐)
自定义比较函数怎么传
当范围按非默认规则排序(如降序、或自定义类型),必须显式传入比较对象:
std::vector<:string> words = {"apple", "banana", "cherry"};
// 按字符串长度排序
std::ranges::sort(words, {}, &std::string::size); // 升序 by length
// 查找长度为 6 的所有字符串
auto r = std::ranges::equal_range(words, 6, {}, &std::string::size);
// 第三个参数 {} 是投影(projection),第四个是比较(comp),顺序固定</:string>
常见错误:
- 漏掉空花括号
{}:编译器无法推导投影类型,报错类似no matching function for call to 'equal_range' - 把比较函数和投影位置颠倒:
equal_range(v, val, comp, proj)是错的,正确是equal_range(v, val, comp, proj)?不对——实际签名是equal_range(rng, value, comp={}, proj={}),但comp默认是std::ranges::less,所以投影必须显式写成第四参数,第三参数留空或显式传std::ranges::less{} - 误用 lambda 作为投影却没捕获:比如
[&](const auto& x) { return x.id; }如果x是临时对象,可能引发悬垂引用
性能和兼容性要注意什么
std::ranges::equal_range 底层仍依赖随机访问迭代器的 O(log n) 时间复杂度。但它不适用于 std::list 或 std::forward_list —— 这些容器不满足 std::ranges::random_access_range 要求,调用会编译失败(SFINAE 排除)。
兼容性陷阱:
- MSVC 19.3x 早期版本对
subrange结构化绑定支持不稳定,建议用/std:c++20且更新到最新工具集 - Clang 14+、GCC 12+ 支持完整,但 GCC 11 默认不启用 Ranges TS 完整特性,需加
-fconcepts - 如果你用的是 C++17 项目,别试图用
std::ranges::命名空间——它不存在,得退回老式std::equal_range(first, last, val)
最容易被忽略的是:它不检查范围是否真有序。传入乱序容器不会报错,但结果完全不可靠——调试时得自己加 assert(std::ranges::is_sorted(v)) 临时验证。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











