<p>std::midpoint 是 C++20 引入的安全计算中点函数,避免整数溢出导致的未定义行为;(a + b) / 2 对有符号整数可能溢出,引发 UB;a + (b - a) / 2 虽避溢出但需 a ≤ b。</p>

直接说结论:std::midpoint 是 C++20 引入的、专为安全计算两个整数(或指针)中点而设计的函数,它能天然避免 a + b 溢出导致的未定义行为,比手写 (a + b) / 2 或 a + (b - a) / 2 更可靠、更语义清晰。
为什么 (a + b) / 2 在整数上危险?
对有符号整数,a + b 溢出是未定义行为(UB),编译器可能优化掉你的判断逻辑,甚至生成错误结果。例如 INT_MAX + 1 不是 INT_MIN,而是彻底失控。
常见误用场景包括二分查找边界更新、数组索引中点计算、归并排序分治点等。
- 手写
a + (b - a) / 2虽可规避溢出,但需确保a ,否则 <code>b - a可能溢出(如a = INT_MIN, b = 0) -
std::midpoint内部使用位运算或条件分支处理所有符号组合,不依赖加法,也不要求大小顺序 - 它对
unsigned、signed、std::byte和指针类型都重载支持
std::midpoint 的参数和类型约束
它接受两个同类型的算术类型或指针,返回相同类型。不支持浮点数(浮点中点本就不溢出,且 (a + b) * 0.5 通常足够)。
立即学习“C++免费学习笔记(深入)”;
- 合法调用:
std::midpoint(1, 3)、std::midpoint(-100, 50)、std::midpoint(ptr1, ptr2) - 非法调用:
std::midpoint(1.0, 2.0)(编译失败)、std::midpoint(1U, -2)(类型不匹配) - 对指针,要求两者指向同一数组(或末尾),否则行为未定义 —— 这和
std::distance一致,不是midpoint特有缺陷
和手写 a + (b - a) / 2 的实际差异
表面看两者数学等价,但语义与边界行为不同。尤其当 a 和 b 符号相反时:
int a = INT_MIN; // -2147483648 int b = 1; // 手写:a + (b - a) / 2 → b - a 溢出(UB) // std::midpoint(a, b) → 正确返回 -1073741823(向下取整)
-
std::midpoint对整数始终向下取整(即向负无穷舍入),和(a + b) / 2在非负数时一致,但在负数奇数和时更可预测 - 它不依赖编译器对溢出表达式的“宽容实现”,在严格模式(如
-ftrapv)下依然稳定 - 性能无显著开销:现代实现通常是几条位指令,比分支判断还快
容易被忽略的兼容性与迁移细节
如果你项目还没上 C++20,别硬凑宏模拟 —— 很多“安全中点”宏仍隐含 UB 或漏掉指针重载。
- Clang/GCC/MSVC 均从 C++20 起支持,但需显式启用
-std=c++20(MSVC 默认可能仍是 C++17) - 头文件只需
<algorithm>,无需额外包含 - 对旧标准,可用
std::midpoint的标准实现参考(来自 libstdc++ 或 libc++ 源码),但务必测试INT_MIN/INT_MAX组合 - 注意:它不处理自定义数值类,若需扩展,必须特化
std::midpoint并满足其语义要求(如结合律、对称性)
最麻烦的从来不是写对一行代码,而是确认你调用它的那两个变量,是否真在同一个可比较、可线性映射的域里 —— 比如混用不同内存池的指针,或把枚举值当整数传进去。


















