std::midpoint是c++20唯一标准保证不触发有符号整数加法溢出的中点计算工具,仅当参数为同类型有符号/无符号整数或同数组原生指针时真正安全;对浮点数无效,混合类型或非法指针会编译失败或引发未定义行为。

直接用 std::midpoint 替换 (a + b) / 2 并不能自动规避溢出——它只在整型同号、类型严格一致、指针同数组等硬约束下才真正生效;盲目替换可能编译失败,或在浮点/混合类型场景引入更隐蔽的精度错误。
什么时候 std::midpoint(int, int) 真正防溢出
它防的是有符号整数加法中间步骤的未定义行为(UB),不是“结果太大”。例如 std::midpoint(INT_MAX, INT_MAX - 1) 返回确定值 INT_MAX - 1,而 (INT_MAX + (INT_MAX - 1)) / 2 是 UB。
- 底层等价于
a + (b - a) / 2(对同号整数),全程避开a + b峰值 - 支持
std::midpoint(INT_MIN, INT_MAX)→ 返回-1,结果数学正确 - 对
size_t等无符号类型也安全,但b - a按模回绕——你要清楚这是否符合业务语义(比如中点不该是回绕后的地址) - 不处理负数舍入以外的业务逻辑:例如
std::midpoint(-3, 2)是-1(向零取整),不是0或-0.5
std::midpoint 在指针计算中不可替代的原因
这是它唯一被标准强制要求规避算术溢出的场景:计算大数组内两个指针的中点地址时,std::midpoint(p1, p2) 要求实现绕过 p2 - p1 可能超出 ptrdiff_t 表示范围的风险。
- 必须是同一数组(或其中一个是
one-past-the-end),如std::midpoint(arr, arr + N) - 拒绝
void*、const int*与int*混用、跨分配块指针——类型和可达性都在编译期卡死 -
std::midpoint(&v[0], &v[0] + v.size())合法;std::midpoint(v.begin(), v.end())编译失败(迭代器不是原生指针) - 空容器必须提前判断:
v.empty()为真时,&v[0]本身非法,std::midpoint不兜底
哪些常见写法会直接编译失败
std::midpoint 对类型极其苛刻,不尝试隐式转换,只做精确匹配。
-
std::midpoint(10, 20U):int和unsigned int类型不一致 → 编译失败 -
std::midpoint(1L, 1):long和int不匹配 → 编译失败 -
std::midpoint(1.5f, 2.5f):虽 C++20 允许浮点重载,但底层仍是(a + b) / 2,无安全增益,且量级差异大时精度更差 - 未启用 C++20:GCC/Clang 默认不可见该函数,需显式加
-std=c++20;MSVC 需 19.29+ 且设/std:c++20
浮点坐标别用 std::midpoint 做“安全平均”
它对 float 或 double 坐标没有额外防护能力,反而可能掩盖精度问题。
-
std::midpoint(1e30f, 1.0f)极大概率返回1e30f,因为1e30f + 1.0f == 1e30f(加法丢失低位) - 真正抗量级差异的写法是分支判断:
std::abs(a) >= std::abs(b) ? a + (b - a) * 0.5f : b + (a - b) * 0.5f - 若需更高精度保障,优先用
std::fma(a, 0.5f, b * 0.5f),而非依赖std::midpoint -
std::midpoint对NaN、inf无特殊处理,和裸算一样传播异常值
最易被忽略的一点:它只保证“给定两个合法值,中点算得对”,不校验输入是否有效——比如传进去两个悬空指针,它仍可能返回一个地址;传进去越界索引,它也不报错。安全边界仅限数值/指针算术本身,语义层的合法性全靠你兜底。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











