c++oding="utf-8" ?>
std::midpoint仅防止中点计算中a+b导致的有符号整数加法溢出,不防输入非法、浮点精度损失、类型不匹配或指针不可达等场景。

std::midpoint 是 C++20 唯一能从标准层面保证「整数相加不溢出」的中点计算工具,但它不是万能胶——用错类型、错估场景或忽略编译器支持,照样崩。
std::midpoint 能防什么溢出,不能防什么
它只防「中点计算过程中因 a + b 导致的有符号整数加法溢出」。例如 INT_MAX 和 INT_MAX - 1 相加会触发未定义行为(UB),而 std::midpoint(INT_MAX, INT_MAX - 1) 内部走 a + (b - a) / 2 或位运算路径,全程无加法峰值,结果确定且安全。
但它不防:
- 输入值本身已非法(比如从网络读入的
INT_MIN - 1,这在读取阶段就 UB 了) b 时的业务逻辑错误(它仍会返回⌊(a+b)/2⌋,但可能指向无效内存)- 浮点数的精度丢失或溢出(
std::midpoint(1e30f, 1.0f)仍是(a + b) / 2,不会自动切到std::fma) - 指针跨数组使用(
std::midpoint(p1, p2)要求p1和p2属于同一数组,否则未定义)
编译失败?先查这三个硬条件
常见报错 'midpoint' is not a member of 'std' 不一定是写错了,大概率是卡在以下任一环节:
- 编译器版本太老:GCC -std=c++20 也白搭
- 没包含头文件:
#include缺一不可 - 参数类型不一致:
std::midpoint(10, 10U)编译失败(intvsunsigned int);std::midpoint(1, 2L)同样失败,必须显式统一,如std::midpoint(1L, 2L)
注意:std::vector::iterator 不是原生指针,std::midpoint(v.begin(), v.end()) 在 C++20 中非法;得用 std::midpoint(std::data(v), std::data(v) + v.size())。
替代方案:C++17 及更早怎么写才真安全
别用 (a + b) / 2,也别信 (a + b) >> 1(负数右移行为实现定义)。可靠 fallback 是:
- 对非负整数(如
size_t索引):(a & b) + ((a ^ b) >> 1)—— 无分支、无溢出、语义等价于向下取整平均值 - 对可能为负的
int:static_cast((static_cast (a) & static_cast (b)) + ((static_cast (a) ^ static_cast (b)) >> 1)) - 二分查找中若已知
low ,可直接手写low + (high - low) / 2,但要注意:对size_t,若high 会导致回绕,所以仅限你 100% 控制区间方向时用
浮点数和指针:唯一不该省略 std::midpoint 的两个场景
对浮点数,std::midpoint 没有实质安全优势,但有两个隐性价值:
- 它强制你意识到「中点」在 IEEE 754 下的舍入一致性(向偶数舍入),比手写
(a + b) * 0.5更可预测 - 当 a 和 b 量级悬殊(如
1e30和1.0),某些标准库实现会对浮点版std::midpoint做分支优化,比裸写(a + b) / 2略稳——但别依赖,真要抗溢出请用std::fma(a, 0.5, b * 0.5)
对指针,它是不可替代的:
-
std::midpoint(begin, end)明确要求实现规避end - begin超出ptrdiff_t范围的未定义行为 - 手写
begin + (end - begin) / 2看似一样,但标准不保end - begin本身不溢出;而std::midpoint是唯一被标准点名「必须安全」的指针中点函数
真正容易被忽略的是:它不做任何运行时校验——传进去两个野指针,它照样算,结果也“合法”,但毫无意义。安全的前提,永远是你自己确保参数在数学和内存语义上成立。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











