模板函数更合理,因其保留原始签名、支持完美转发、编译期推导返回值,并便于sfinae和noexcept判断,避免std::function类型擦除开销及业务代码重复。

重试逻辑该封装成模板函数还是普通函数
直接写模板函数更合理。C++ 的 std::function 虽能包装任意可调用对象,但类型擦除带来额外开销;而模板能保留原始签名、支持完美转发、编译期推导返回值,也方便做 SFINAE 或 noexcept 判断。
常见错误是把重试逻辑写死在业务函数里,导致每个需要重试的函数都复制粘贴 sleep + 循环 + 错误判断 —— 这既难维护,也无法统一退避策略或日志行为。
- 用
template<typename f typename... args></typename>接收可调用对象和参数,避免std::function间接调用开销 - 重试次数、延迟时间、异常/返回值判定逻辑应作为模板参数或函数参数分离出来,别硬编码
- 注意转发:用
std::forward<args>(args)...</args>,否则右值参数会被转成左值,影响移动语义
怎么判断一次调用是否该重试
没有银弹方案,必须由调用方明确指定。C++ 不像 Python 有 @retry 装饰器那种隐式约定,你得显式告诉重试函数:“哪些情况算失败”。否则它无法区分 std::runtime_error 是网络超时(可重试)还是配置错误(不该重试)。
推荐用谓词(predicate)方式传入判断逻辑,比枚举错误码或白名单异常类型更灵活。
- 支持两种判断方式:捕获异常后调用
should_retry_exception(const std::exception&),或检查返回值(如int返回码)调用should_retry_result(int) - 不要默认对所有异常重试 ——
std::bad_alloc或std::logic_error子类通常不该重试 - 若函数返回
std::expected<t e></t>(C++23),可直接解包e做匹配,比 try/catch 更轻量
指数退避(exponential backoff)怎么安全实现
简单乘以 2 容易溢出,且没考虑 jitter(随机扰动)—— 多个客户端同时重试会形成“重试风暴”,压垮下游服务。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
标准库没有现成的退避算法,得自己控制 delay 计算和睡眠精度。
- 用
std::chrono::duration类型做计算,避免整数毫秒硬编码;最大延迟建议设上限(如 5s),防止某次翻倍后卡住几小时 - 每次 delay 加上
std::uniform_int_distribution生成的 ±10% 随机偏移,缓解同步重试 - 睡眠用
std::this_thread::sleep_for(delay),别用sleep(1)这种秒级粗粒度,也别在循环里反复now() - start > timeout自旋 - 如果调用本身带超时(如
std::future::wait_for),需把重试总耗时纳入整体 timeout 控制,否则可能超纲
实际调用时容易忽略的资源与生命周期问题
最常踩的坑不是逻辑写错,而是 lambda 捕获了局部变量或 this 指针,在重试跨线程或延迟后访问已销毁对象。
另一个隐蔽问题是重试期间发生栈展开(stack unwinding),但重试函数本身没标记 noexcept,导致意外调用 std::terminate。
- lambda 捕获列表务必用
[=]或[&]显式声明,禁用默认捕获;涉及成员函数时优先用[this]+ 成员变量显式列出,而非隐式捕获整个对象 - 若被重试函数可能抛异常,重试模板本身不要加
noexcept,除非你 100% 确保内部不抛(比如只用sleep_for和条件判断) - 避免在重试逻辑里 new/delete 或打开文件句柄 —— 重试失败时这些资源可能泄漏;把资源获取放在重试外部,或确保 RAII 正确生效
重试不是万能胶布。超时设置、退避上限、失败归因,每一项都要结合具体协议(HTTP 状态码?RPC 错误码?)和系统边界来定,写死一个“重试 3 次”往往比不写还危险。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










