std::midpoint能规避整数溢出,因其等价于a+(b-a)/2或位运算实现,中间值不超类型范围;对负数也安全,天然向下取整;但仅适用于同类型整型或同数组指针,不校验越界等业务逻辑。

直接用 std::midpoint 替代 (a + b) / 2 能规避整数溢出,但仅当参数是同类型整型(如 int、size_t)或同数组指针时才真正生效;浮点数、混类型、迭代器等场景下它要么不防溢出,要么根本编译不过。
std::midpoint(int, int) 在坐标索引中为何能防溢出
它不计算 a + b,而是等价于 a + (b - a) / 2 或位运算实现(如 (a & b) + ((a ^ b) >> 1)),中间值始终落在类型可表示范围内。
-
std::midpoint(INT_MAX, INT_MAX - 1)返回确定值,无未定义行为;而(INT_MAX + INT_MAX - 1) / 2是 UB - 对负坐标也安全:
std::midpoint(-1000000, -1)正确返回-500000(向下取整语义) - 不要求
a :<code>std::midpoint(10, 2)合法,结果为6 - 若用
size_t表示像素坐标,std::midpoint(0U, SIZE_MAX)结果是模意义下的中点(即SIZE_MAX / 2),业务需确认是否接受该语义
std::midpoint(T*, T*) 是唯一不可替代的指针中点场景
当处理超大数组(如接近地址空间上限)时,手写 p + (q - p) / 2 中的 q - p 可能超出 ptrdiff_t 范围,触发未定义行为;std::midpoint 强制要求实现规避该问题。
- 必须是同一数组内指针:
std::midpoint(arr, arr + N)✅;std::midpoint(ptr1, ptr2)若跨数组 ❌ - 不接受迭代器:
std::midpoint(v.begin(), v.end())编译失败;应改用std::midpoint(std::data(v), std::data(v) + v.size()) - 类型必须完全一致:
const int*和int*混用会编译失败;void*不支持 - 不校验
arr + N是否越界——越界本身已是 UB,std::midpoint只保计算过程安全
哪些情况 std::midpoint 会失效甚至报错
它不是万能函数,很多“看似合理”的调用会直接翻车。
立即学习“C++免费学习笔记(深入)”;
- 类型不匹配:
std::midpoint(1, 1U)、std::midpoint(1, 1L)均编译失败,必须显式统一类型,如std::midpoint(static_cast<long long>(a), static_cast<long long>(b))</long></long> - 浮点数不防溢出:
std::midpoint(1e30f, 1.0f)底层仍可能走(a + b) / 2,加法直接舍入或溢出为inf;真要稳健,得用std::fma(0.5f, a, 0.5f * b) - 参数表达式自身已溢出:如
std::midpoint(a, b + 1)中b + 1先溢出,则std::midpoint无力回天 - 头文件和标准缺失:必须
#include <numeric></numeric>且启用-std=c++20;用<utility></utility>或<cmath></cmath>会导致 “not declared in this scope”
兼容 C++17 及更早版本的安全替代写法
若无法升级到 C++20,可用模板函数模拟语义,关键在于避免加法峰值。
- 对有符号整型,用
a + (b - a) / 2:差值b - a不会比a或b更易溢出 - 对无符号整型,用位运算:
(a & b) + ((a ^ b) >> 1),天然无溢出且等价于向下取整平均值 - 若坐标可能跨符号(如
int但含负值),加分支判断:a = 0 ? (a + b) / 2 : a + (b - a) / 2 - 别依赖
(a + b) >> 1:对负数在补码下行为不一致,且不适用于浮点或大整数
最常被忽略的一点:它只解决“给定两个合法值,如何算中点不溢出”,不校验坐标是否越界、指针是否有效、区间是否为空——这些仍需业务逻辑兜底。


















