std::midpoint是c++20新增的安全求中点函数,老版本需手动实现;它通过无溢出算法避免有符号整数加法ub,支持整型、指针和浮点,而(a+b)/2易导致溢出或编译错误。

标准库里没有 midpoint,C++20 才正式加入,老版本必须自己写或用 std::midpoint 的替代逻辑,否则容易整数溢出。
为什么 std::midpoint 不能直接用(C++17 及更早)
很多项目还卡在 C++14/17,std::midpoint 是 C++20 引入的,头文件是 <numeric></numeric>,但编译器不支持就报 ‘midpoint’ is not a member of ‘std’。别试了,它真不存在。
- Clang 10+、GCC 10+、MSVC 19.28+ 才开始支持 C++20 的
std::midpoint - 即使开了
-std=c++20,旧版编译器也会静默忽略或报错 - 别依赖 IDE 自动补全——它可能从头文件注释或未来标准里“猜”出了这个函数
std::midpoint 和 (a + b) / 2 差在哪
核心区别就一个:是否防溢出。对有符号整数,a + b 可能触发未定义行为(UB),而 std::midpoint 用位运算等价实现,全程不溢出。
-
int a = INT_MAX, b = 1;→(a + b) / 2是 UB(加法溢出) -
std::midpoint(a, b)返回INT_MAX(正确中间值) - 对指针类型,
std::midpoint(p, q)要求p和q指向同一数组,且结果可表示为T*;手写(p + q) / 2根本编译不过 - 浮点数上两者行为一致,但
std::midpoint明确保证舍入方向(向偶数舍入),避免0.1f + 0.2f类精度陷阱
手动实现安全的 midpoint(兼容 C++11)
别抄网上“(a & b) + ((a ^ b) >> 1)”那种只适用于无符号的写法——它对负数完全错误。正确做法分类型处理:
立即学习“C++免费学习笔记(深入)”;
// C++11 兼容,支持整型、指针、浮点
template <class T>
constexpr T safe_midpoint(T a, T b) {
using U = typename std::make_unsigned<T>::type;
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
// 有符号整数:转无符号做算术,避免 UB
return static_cast<T>(static_cast<U>(a) + (static_cast<U>(b) - static_cast<U>(a)) / 2);
} else if constexpr (std::is_pointer_v<T>) {
auto da = b - a;
return a + da / 2;
} else {
return (a + b) / 2; // 浮点安全,且语义一致
}
}- 对
int,safe_midpoint(INT_MIN, INT_MAX)返回-1(正确) - 别用
std::abs或条件分支判断正负——分支影响 constexpr 和性能 - 指针版本依赖
operator-和operator+,确保你传的是同数组指针,否则行为未定义 - 如果项目已用 C++20 且确认编译器支持,直接用
std::midpoint,它做了更多边界检查(比如对nullptr)
常见误用和调试线索
看到二分查找 crash 或结果偏移,先查是不是用了裸 (low + high) / 2。这不是风格问题,是潜在崩溃点。
- 错误现象:
std::vector::at()报out_of_range,但下标明明在[0, size)内 → 很可能是mid算错了 - Clang 编译加
-fsanitize=undefined会直接捕获signed integer overflow - 用
std::midpoint时传入不同类型的参数(如int和long)会编译失败,必须显式转换 - 模板推导失败常见于指针:传
&arr[0]和&arr[N]是int*,但若数组是const int[],类型是const int*,必须一致
最麻烦的不是写不对,是写对了但忘了所有调用点都得换——尤其封装在工具函数里时,漏一处就白干。









