std::midpoint能安全计算同类型整型、浮点或同数组指针的中点,避免溢出;但要求参数类型严格一致、仅支持算术类型或同类型原生指针,且不校验区间有效性。

std::midpoint 能彻底解决整型中点计算的溢出问题,但只在类型匹配、同号整数或合法指针场景下才真正安全;浮点数上它不防溢出也不提精度,盲目替换 (a + b) / 2 反而可能引入错误。
为什么 (a + b) / 2 在二分查找里会崩
常见错误现象是:索引 left 和 right 都接近 INT_MAX,比如 left == INT_MAX - 1、right == INT_MAX,此时 left + right 直接溢出为负数,后续除法结果完全不可预测,轻则越界访问,重则死循环。
- 该问题在
size_t或uint64_t上不会崩溃(无符号加法回绕),但语义错误:中点不该是回绕后的值 - 编译器通常不报错,运行时行为未定义(UB),静态分析工具如 clang-tidy 的
bugprone-integer-division规则也未必能捕获 - 手写
a + (b - a) / 2可缓解,但对负数、无符号类型、浮点仍需额外判断
std::midpoint 正确调用的三个硬性条件
它不是万能胶水函数,不满足以下任一条件就会编译失败或行为未定义:
- 两个参数必须是**完全相同类型**:
std::midpoint(1, 1U)、std::midpoint(1L, 1)均编译不过;需显式统一,如std::midpoint(static_cast<long long>(a), static_cast<long long>(b))</long></long> - 仅支持算术类型(
int、long long、float、double)或**同类型原生指针**(int*、char*);std::vector::iterator、std::unique_ptr等不支持 - 指针版本要求
b可从a到达(即a 且指向同一数组),否则未定义;<code>std::midpoint(arr, arr + N)合法,std::midpoint(p1, p2)(不同分配块)非法
浮点数用 std::midpoint 反而更危险
标准只要求它对浮点类型提供重载,但底层仍是 (a + b) / 2,不规避溢出、不改善精度。当 a 和 b 量级悬殊时,问题暴露得更明显:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
-
std::midpoint(1e30f, 1.0f)极大概率返回1e30f(因为1e30f + 1.0f == 1e30f,加法丢失低位) - 而手工写
a + (b - a) * 0.5f或用std::fma(a, 0.5f, b * 0.5f)更可靠 - 若真需抗溢出浮点中点,优先检查量级:
if (std::abs(a) >= std::abs(b)) { return a + (b - a) * 0.5f; } else { return b + (a - b) * 0.5f; }
指针中点是它唯一不可替代的用途
这是 std::midpoint 最独特、最实用的场景——计算大数组边界内的中间地址,且由标准强制保证不触发指针算术溢出:
- 传统写法
begin + (end - begin) / 2中,end - begin若超过ptrdiff_t表示范围,就是未定义行为;std::midpoint(begin, end)明确要求实现绕过该减法峰值 - 仅接受
T*类型,不接受void*或const T*与T*混用;std::midpoint(&v[0], &v[0] + v.size())合法,std::midpoint(v.data(), v.data() + v.size())同样合法 - 它不校验
begin和end是否有效、是否同数组——这些仍是你的责任
真正容易被忽略的是:它只解决「给定两个合法值,怎么算中点」这一个子问题。数组是否为空、迭代器是否失效、区间是否非空——这些都得靠你自己的逻辑兜底。它不抛异常、不返回错误码、不做断言,只是默默给出数学上最稳妥的结果。

















