用多重捕获与强编译约束将分支覆盖检查提前至编译期:以std::variant+std::visit穷举补偿动作,concept约束接口契约,noexcept/explicit封禁错误路径,C++23反射自动生成校验与文档。

直接用多重捕获配合强编译约束,不是为了炫技,而是把“分支逻辑是否覆盖完全”这件事从运行时崩溃提前到编译时报错。核心在于:让编译器替你检查所有可能路径,而不是靠人肉 review 或测试用例去碰运气。
用 std::variant + std::visit 构建可穷举的补偿分支
传统 if-else 或 switch 处理多状态补偿(如订单失败后的退款、重试、告警、降级)容易漏分支、加新状态要改多处、难以静态验证。换成 variant,就把所有合法状态变成类型系统的一部分:
- 定义一个
CompensationActionvariant,包含所有已知补偿动作类型(Refund、RetryWithBackoff、NotifyOps、ApplyFallback) - 所有生成该 variant 的地方(比如策略引擎)必须显式返回其中一种,不能返回
void或裸指针 - 消费端必须用
std::visit完全覆盖每种类型——少写一个std::visit的 overload,编译就失败
用 concept 约束补偿动作的接口契约
光有类型枚举不够,还得保证每个补偿动作都满足基本行为契约。例如,所有动作都必须能提供可读名称、支持超时控制、允许取消:
- 定义
concept CompensationAction,要求类型具备name()、execute(std::stop_token)、timeout_duration()成员 - 每个具体动作 struct 显式声明满足该 concept(
static_assert(CompensationAction<Refund>);) - 策略函数返回
std::variant<Refund, RetryWithBackoff, ...>时,编译器会自动校验每个子类型都满足 concept,否则报错
用 noexcept 和 explicit 封死隐式错误路径
补偿逻辑常涉及资源释放或关键状态回滚,绝不允许意外异常中断或隐式转换引入歧义:
- 所有补偿动作的
execute()声明为noexcept;若内部可能抛异常,必须在动作构造时捕获并转为状态字段(如result = std::unexpected(error_code)),而非放任传播 - 避免单参数构造函数隐式转换:比如
Refund(int amount)改为explicit Refund(int amount),防止process(42)这类调用悄悄构造出错误动作 - 对 variant 的构造做 SFINAE 限制:只允许从明确支持的枚举值或动作类型构造,禁止
CompensationAction{42}这类模糊初始化
用 编译期反射(C++23)自动生成注册与校验逻辑
当补偿分支增多,手动维护 std::visit 分支或工厂映射易出错。可用宏或 C++23 反射机制生成:
- 为每个动作类型添加
[[reflect]]属性或专用 trait - 编写一个
constexpr函数,遍历所有已注册动作类型,生成完整std::visit分支列表,并在编译期断言其 size 与 variant 的size()一致 - 配套生成 JSON Schema 或 OpenAPI 描述,供运维平台动态加载动作元信息,实现“代码即文档、代码即配置”

















