该用 std::function 而不是函数指针,因其能统一包装任意可调用对象(如带捕获 lambda、绑定成员函数),而函数指针无法支持;重试机制需兼容多种签名,且其开销远小于重试本身。

什么时候该用 std::function 而不是函数指针?
直接传函数指针会丢失模板类型信息,导致无法捕获 lambda 或带捕获的可调用对象。重试机制必须支持任意签名的可调用体,所以得用 std::function 作统一包装。
-
std::function<void></void>可以容纳无参 lambda、普通函数、绑定后的成员函数 - 如果硬用
void(*)(),遇到[&]() { ... }就编译失败 - 别省那点开销——重试本身已是重量级操作,
std::function的间接调用成本可忽略
std::this_thread::sleep_for 的等待时间怎么设才不伤性能?
固定延时(如每次等 100ms)在高并发或瞬时抖动场景下容易放大延迟;指数退避才是合理选择。
- 初始 delay 设为
std::chrono::milliseconds(10),每次失败后 ×2,上限建议std::chrono::seconds(5) - 务必加随机抖动(jitter),比如 ±10%,避免大量重试请求同时砸向下游服务
- 别用
sleep_for等待 0ms——某些平台调度不保证立即唤醒,可能意外跳过重试逻辑
异常类型过滤为什么不能只靠 catch(...)?
盲目捕获所有异常会吞掉本该中止程序的致命错误(如 std::bad_alloc、栈溢出信号),也违背“只重试可恢复错误”的设计原则。
- 明确列出可重试的异常类型:比如自定义的
NetworkError、TransientDbError,或标准库中的std::system_error(且需检查.code().category() == std::generic_category()) - 用
if (e.code().value() == ECONNREFUSED || e.code().value() == ETIMEDOUT)进一步缩小范围 - 遇到未预期异常(如
std::logic_error),直接 rethrow,不计入重试次数
模板参数里要不要暴露 Duration 类型?
要。把等待单位硬编码成 std::chrono::milliseconds 会限制使用场景——有人想用微秒做精细控制,有人想用秒级做长任务兜底。
- 模板声明写成
template<typename f typename duration="std::chrono::milliseconds"></typename> - 调用时可显式指定:
retry_with_backoff<yourfunc std::chrono::microseconds>(f, 3, {50})</yourfunc> - 注意:别让
Duration和实际传入的数值单位错配,例如传std::chrono::seconds(2)却用Duration = std::chrono::milliseconds,会导致等待 2 毫秒而非 2 秒
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











