最可靠做法是在构造函数中用std::numeric_limits进行边界校验并抛出std::out_of_range;算术操作前须重校验中间结果,优先使用c++23的std::add_overflow等函数;字符串转换时需严格对齐strtoll的erange检查与numeric_limits兜底。

直接在类内部用 std::numeric_limits 做校验,而不是依赖外部传入的“已知安全值”——这是最可靠、最可维护的做法。
构造函数里就该拦住越界输入
很多封装类(比如 SafeInt32、BrightnessValue)把边界检查推迟到运算时,结果是错误被掩盖、传播到下游才暴露。正确做法是在构造时就做一次彻底校验。
- 用
std::numeric_limits<t>::min()</t>和std::numeric_limits<t>::max()</t>获取类型合法范围,不是硬写-2147483648这类魔数 - 对浮点类型,还要额外考虑
std::numeric_limits<t>::lowest()</t>(它比min()更负,适用于非正规数场景) - 如果输入超出范围,抛出
std::out_of_range而不是静默截断——静默行为会让调用方误以为数据有效
算术操作前必须重校验中间结果
即使构造时合法,operator+ 或 operator*= 仍可能产生溢出。别指望编译器或运行时自动捕获——C++ 对无符号整数溢出定义为回绕(well-defined),对有符号则是未定义行为(UB)。
- 加法前:检查
a > std::numeric_limits<t>::max() - b</t>(避免先加再判断的溢出风险) - 乘法前:对非零操作数,用除法反推是否超限,例如
b != 0 && a > std::numeric_limits<t>::max() / b</t> - 优先使用
std::add_overflow、std::mul_overflow(C++23 起)这类标准溢出检测函数,比手写条件更健壮
和字符串转换交互时,边界逻辑要对齐
封装类常提供 from_string() 或隐式转换构造函数,这时边界处理必须和底层 strtol/strtoull 的 errno + ERANGE 行为一致,否则会出现“能转出来却非法”的矛盾。
- 调用
std::strtoll后,必须检查errno == ERANGE,且返回值等于LLONG_MAX或LLONG_MIN - 不要只看
endptr是否移动了——无效输入(如空串、纯空白)也会让endptr == nptr,这和溢出是两类错误 - 若底层转换函数返回极值但没设
errno(某些旧 libc 实现),需额外用std::numeric_limits做兜底校验
真正容易被忽略的是:边界值处理不能只盯着“最大最小”,还要覆盖浮点的 NaN、inf,以及有符号整数的 INT_MIN 取负溢出(-INT_MIN 仍是 INT_MIN)。这些不是边缘 case,而是真实数据流中高频出现的坑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











