应使用 std::midpoint 替代 (a + b) / 2 计算整数中点,因其在 c++20 中被明确定义为不溢出;需包含 、启用 c++20 标准且编译器版本达标,注意其向下取整语义、类型严格匹配及指针中点的安全优势。

别用 (a + b) / 2 算整数中点——它在 INT_MAX 和 INT_MAX - 1 这类输入下直接触发未定义行为(UB),不是“结果错”,而是编译器可能删代码、优化出不可预测分支。用 std::midpoint 是 C++20 中唯一被标准明确定义「不溢出」的替代方案,但它只在特定类型和调用条件下真正安全。
std::midpoint 编译不过?先查这三件事
最常见失败不是函数写错,而是环境或头文件没配对:
- 漏掉
#include <numeric></numeric>—— 它不在<iostream></iostream>或<cmath></cmath>里,缺了就报'midpoint' is not a member of 'std' - 编译器没开 C++20:
MSVC需加/std:c++20,GCC/Clang必须传-std=c++20(CMake 中设set(CMAKE_CXX_STANDARD 20)) - 编译器版本太低:
MSVC ≥ 19.28(VS 2019 v16.8+)、GCC ≥ 10、Clang ≥ 11才完整支持;旧版即使加了 flag 也链接失败
std::midpoint(int, int) 的行为边界必须清楚
它返回的是向下取整的中点 ⌊(a+b)/2⌋,不是四舍五入,也不是浮点平均值:
-
std::midpoint(-3, 2)返回-1(不是0),因为floor((-3+2)/2) == floor(-0.5) == -1 -
std::midpoint(0, 1)返回0(不是0.5),结果类型与参数完全一致,不隐式转浮点 - 允许
a > b:std::midpoint(5, 1)合法且返回3 - 类型严格匹配:
std::midpoint(10U, 20)编译失败(unsigned intvsint),不自动转换
指针中点才是 std::midpoint 不可替代的场景
这是它在整个标准库中唯一无法被手写逻辑安全替代的用途:计算超大数组内两个指针的中间地址,彻底规避 end - begin 超出 ptrdiff_t 表示范围导致的未定义行为:
- 正确:
auto mid = std::midpoint(arr, arr + N);,即使N接近SIZE_MAX / sizeof(T)也安全 - 错误:
std::midpoint(v.begin(), v.end())编译失败(迭代器不是原生指针),得写成std::midpoint(std::data(v), std::data(v) + v.size()) - 必须指向同一数组(或 one-past-the-end),
std::midpoint(p1, p2)若跨数组、含nullptr或悬空指针,行为未定义(编译器不报错) - 不支持
void*,也不接受const int*和int*混用(除非显式const_cast,但不推荐)
float/double 上用 std::midpoint 反而更危险
C++20 确实提供了浮点重载,但它只是语法存在,底层仍是 (a + b) / 2,既不防溢出,也不改善精度:
-
std::midpoint(1e30f, 1.0f)中1e30f + 1.0f直接舍入为1e30f,结果错误 - 强制转浮点再传入(如
std::midpoint(static_cast<float>(a), static_cast<float>(b))</float></float>)会丢失整数精度,还放弃整数版的安全性 - 真需稳健浮点中点,优先用
std::fma(a, 0.5, b * 0.5)(需#include <cmath></cmath>),或按量级做分支判断 - 别为了“统一写法”硬套——浮点中点本就不该依赖
std::midpoint
最容易被忽略的是:它只保证「给定两个合法值时,中点计算过程不溢出」,不校验参数本身是否越界、容器是否为空、指针是否有效——这些仍需业务层兜底。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











