应使用 std::lower_bound 和 std::upper_bound 组合获取目标值在有序 vector 中的左闭右开出现范围:lower_bound 返回首个 ≥ target 的位置,upper_bound 返回首个 > target 的位置,二者构成 [lo, hi) 区间;需先验证 lo < hi 再解引用或遍历,且自定义比较谓词必须完全一致。

用 std::lower_bound 和 std::upper_bound 找出值的范围
在已排序的 std::vector 中查找某个值的所有出现位置(即左闭右开区间),不能靠 std::find 逐个扫——它不利用有序性,时间复杂度是 O(n)。正确做法是组合使用 std::lower_bound 和 std::upper_bound,二者都是 O(log n),且返回迭代器。
它们的区别在于:
-
std::lower_bound返回第一个 ≥ target 的位置 -
std::upper_bound返回第一个 > target 的位置
所以 [lower_bound, upper_bound) 就是所有等于 target 元素的合法范围。
示例:
立即学习“C++免费学习笔记(深入)”;
std::vector<int> v = {1, 2, 2, 2, 3, 4, 4};
auto lo = std::lower_bound(v.begin(), v.end(), 2);
auto hi = std::upper_bound(v.begin(), v.end(), 2);
// lo 指向索引 1,hi 指向索引 4 → 共 3 个 2
检查范围是否为空:别直接解引用 end()
如果目标值不在 vector 中,lower_bound 可能返回 v.end();此时若直接用 *lo 或比较 lo < hi 前没做验证,会崩溃或行为未定义。
安全写法是先判断范围有效性:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用
lo != v.end() && *lo == target确认存在至少一个匹配项 - 或更稳妥地:只依赖
lo < hi判断非空(因为即使lo == v.end(),hi也一定等于它,此时lo < hi为 false) - 注意:
std::distance(lo, hi)是安全的,哪怕范围为空
错误示范:if (*lo == target) —— 若 lo == v.end(),解引用必崩。
自定义比较时,两个函数必须用同一套逻辑
如果你传了自定义比较函数(比如按 struct 的某个字段排序),lower_bound 和 upper_bound 必须用**完全相同的谓词**,否则范围可能错乱。
例如:
struct Person { int age; std::string name; };
bool cmp(const Person& a, const int& b) { return a.age < b; } // 注意参数顺序
auto lo = std::lower_bound(v.begin(), v.end(), 25, cmp);
auto hi = std::upper_bound(v.begin(), v.end(), 25, cmp); // 必须同个 cmp!
常见坑:
- 误把
upper_bound的谓词写成a.age <= b—— 这违反严格弱序,结果不可预测 - 谓词参数顺序写反(比如
int, Person&),编译不过或静默错误 - 用 lambda 时捕获变量导致生命周期问题(尤其在 vector 是局部变量时)
想返回下标?别手动减 begin(),用 std::distance
直接用 lo - v.begin() 看似简单,但只对随机访问迭代器安全(vector 满足),而 std::distance 是通用且语义清晰的选择。
而且它在 debug 模式下可能带边界检查(取决于标准库实现),比裸指针运算更健壮。
- 获取起始下标:
std::distance(v.begin(), lo) - 获取长度:
std::distance(lo, hi) - 避免写
hi - lo—— 虽然对 vector 有效,但可读性差,且换容器(如deque)就失效
如果后续要批量处理这个范围内的元素,直接用迭代器区间构造新容器或传给算法(如 std::for_each(lo, hi, ...))更自然,不必转下标。

















