C++中区间开闭性必须用<和<=明确表达,不可依赖注释;左闭右开区间[a,b)对应a<=x&&x<b,std::lower_bound与upper_bound默认支持该语义。

用 < 和 <= 明确表达开闭性,别靠注释猜
C++ 没有原生的区间类型,所谓“开区间”“闭区间”全靠你写的比较运算符组合来定义。写错一个符号,逻辑就反了——比如本该排除端点却包含它,或者反之。最常见错误是把 [a, b) 写成 a <= x && x < b(正确),却误写为 a < x && x < b(变成开区间)或 a <= x && x <= b(变成闭区间)。
记住口诀:左括号方括号 → 用 <=;左括号圆括号 → 用 <;右同理。别依赖注释说明“这是左闭右开”,运行时没人读注释。
std::lower_bound 和 std::upper_bound 默认操作的是左闭右开区间
这两个函数是 STL 区间操作的基石,但它们的语义隐含了区间约定:对已排序容器 v,std::lower_bound(v.begin(), v.end(), x) 返回第一个 >= x 的位置,std::upper_bound 返回第一个 > x 的位置。两者配合得到的迭代器范围 [lower, upper) 就是标准的左闭右开区间——包含所有等于 x 的元素,且不越界。
- 想查
x出现几次?直接std::distance(lower, upper) - 想遍历所有
x?用for (auto it = lower; it != upper; ++it)—— 注意是!=,不是<=,因为upper是“过去最后一个”的哨兵 - 如果强行改成闭区间语义(比如用
<=判定上界),会越界或漏掉末尾元素
自定义区间类时,构造函数参数顺序和成员变量命名必须反映开闭状态
有人写 Interval(double a, double b),但没说清楚是 [a,b] 还是 [a,b),调用方只能翻文档甚至源码。更糟的是,内部用 m_left 和 m_right 存值,却不存开闭标志,导致 contains(x) 实现无法适配不同需求。
立即学习“C++免费学习笔记(深入)”;
推荐做法:
- 构造函数显式带布尔参数:
Interval(double a, bool left_inclusive, double b, bool right_inclusive) - 或用枚举:
enum class Bound { Closed, Open };然后Interval(double a, Bound l, double b, Bound r) - 避免重载
operator==直接比端点——两个数学上等价的区间(如[1.0, 2.0)和[1.0, 2.0]在2.0处行为不同)不该被视为相等
浮点数区间判断要小心精度导致的开闭失效
写 if (x >= a && x < b) 看似稳妥,但若 a 或 b 来自计算(比如 sqrt(2.0) * sqrt(2.0)),可能因舍入误差让本该等于 a 的 x 实际略小,导致 x >= a 为假——闭端点意外被排除。
应对方式取决于场景:
- 对容差敏感的业务(如图形裁剪、物理模拟),改用带 epsilon 的比较:
if (x >= a - eps && x < b + eps),但需同步调整开闭逻辑,否则引入新歧义 - 若端点本身来自整数或精确常量(如
0.0,1.0,M_PI),通常无需 epsilon - 最安全的做法:统一用整数或定点数表示关键区间(如时间用毫秒 int,坐标用像素 int),避开浮点开闭模糊地带
开闭不是语法糖,是语义契约。同一段代码里混用 < 和 <= 却不加约束,比没写注释还危险。


















