std::midpoint仅对同类型有符号整型和同数组内指针安全,防中间加法溢出及指针差值越界;混用类型、无符号坐标、浮点数或跨数组指针将导致编译失败或未定义行为。

std::midpoint 不是“更安全的 (a + b) / 2”,它只在整型类型严格匹配、符号合理、且不用于浮点坐标时才真正防加法溢出;盲目替换会编译失败,或在 size_t 坐标、负数区间、跨类型传参等场景引入未定义行为或语义错位。
std::midpoint(int, int) 防的是什么溢出
它防的是有符号整数加法中间步骤的溢出,不是结果太大。比如 std::midpoint(INT_MAX, INT_MAX - 1) 返回确定值 INT_MAX - 1,而 (INT_MAX + (INT_MAX - 1)) / 2 触发未定义行为(UB)。底层等价于 a + (b - a) / 2,全程避开 a + b 的峰值。该路径仅对同符号、同类型的有符号整型(如都是 int 或都显式转为 long long)被标准保证安全。
- 支持
a > b:如std::midpoint(10, 2)得6,数学语义正确,但需确认业务是否允许逆序输入 - 结果向下取整:
std::midpoint(-5, 2)返回-2,与 C++ 整除一致,不是向零取整 - 不提升类型:
std::midpoint(short{1}, short{3})返回short,避免隐式转换带来的截断风险
传 size_t 坐标时为什么结果反直觉
std::midpoint 对无符号类型(如 size_t、uint32_t)也合法,但减法按模算术回绕——std::midpoint(1U, 0U) 返回 0U,因为 (1U - 0U) / 2 == 0,再加 0U。这在环形缓冲区或哈希桶索引中可能符合预期,但在普通二维网格坐标中会违背直觉。
- 若坐标来自容器大小(如
v.size()),且可能接近SIZE_MAX,必须明确业务是否接受“模意义下中点” - 不要混用:
std::midpoint(i, v.size())(i是int,v.size()是size_t)直接编译失败 - 统一类型推荐写法:
std::midpoint(static_cast<size_t>(left), right)</size_t>,但需先确认left >= 0 - 对负坐标场景(如地图偏移量),
size_t不适用,必须用有符号类型
std::midpoint(T*, T*) 是唯一不可替代的用途
这是它最刚性、最不可替代的价值点:计算大数组边界指针的中点时,std::midpoint(begin, end) 被标准强制要求规避 end - begin 超出 ptrdiff_t 范围导致的未定义行为;而手写 begin + (end - begin) / 2 不保这个减法安全。
- 必须是同一数组内指针(含 one-past-the-end):
std::midpoint(&v[0], &v[0] + v.size())✅,std::midpoint(&v[0], &w[0] + w.size())❌(跨容器) - 迭代器不支持:
std::midpoint(v.begin(), v.end())编译失败;应改用std::midpoint(std::data(v), std::data(v) + v.size()) - 空容器须提前检查,否则
&v[0]本身已非法;std::midpoint不兜底 - 类型和 cv 修饰必须精确匹配:
int*与const int*混用直接编译失败
哪些写法会直接编译失败或行为未定义
它不静默降级,而是拒绝编译或触发未定义行为。常见失效点包括环境不达标、类型不一致、指针越界。
- 没启用 C++20:
gcc/clang必须加-std=c++20,MSVC需/std:c++20;否则函数不可见 - 类型不匹配:
std::midpoint(1, 1U)、std::midpoint(1L, 1)、std::midpoint(short{1}, int{2})全部编译失败 - 浮点数滥用:
std::midpoint(1e30f, 1.0f)底层仍是(a + b) / 2,加法直接舍入为1e30f,结果错误;此时应改用std::fma(x, 0.5, y * 0.5) - 跨数组指针、
void*、悬空指针、nullptr:不报编译错,但运行时行为未定义
最易被忽略的是:它不校验指针可达性、不检查容器是否为空、不处理浮点精度、也不自动统一类型——所有这些,都得你手动把关。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











