c++中区间开闭性必须用

用 和 <code> 明确表达开闭性,别靠注释猜
C++ 没有原生的区间类型,所谓“开区间”“闭区间”全靠你写的比较运算符组合来定义。写错一个符号,逻辑就反了——比如本该排除端点却包含它,或者反之。最常见错误是把 [a, b) 写成 a (正确),却误写为 <code>a (变成开区间)或 <code>a (变成闭区间)。
记住口诀:左括号方括号 → 用 ;左括号圆括号 → 用 <code>;右同理。别依赖注释说明“这是左闭右开”,运行时没人读注释。
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)—— 注意是!=,不是,因为 <code>upper是“过去最后一个”的哨兵 - 如果强行改成闭区间语义(比如用
判定上界),会越界或漏掉末尾元素
自定义区间类时,构造函数参数顺序和成员变量命名必须反映开闭状态
有人写 Interval(double a, double b),但没说清楚是 [a,b] 还是 [a,b),调用方只能翻文档甚至源码。更糟的是,内部用 m_left 和 m_right 存值,却不存开闭标志,导致 contains(x) 实现无法适配不同需求。
推荐做法:
- 构造函数显式带布尔参数:
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 看似稳妥,但若 <code>a 或 b 来自计算(比如 sqrt(2.0) * sqrt(2.0)),可能因舍入误差让本该等于 a 的 x 实际略小,导致 x >= a 为假——闭端点意外被排除。
应对方式取决于场景:
- 对容差敏感的业务(如图形裁剪、物理模拟),改用带 epsilon 的比较:
if (x >= a - eps && x ,但需同步调整开闭逻辑,否则引入新歧义 - 若端点本身来自整数或精确常量(如
0.0,1.0,M_PI),通常无需 epsilon - 最安全的做法:统一用整数或定点数表示关键区间(如时间用毫秒 int,坐标用像素 int),避开浮点开闭模糊地带
开闭不是语法糖,是语义契约。同一段代码里混用 和 <code> 却不加约束,比没写注释还危险。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











