将std::expected作为输入参数是否属于反模式?
关于
std::expected作为函数参数的设计疑问 先贴出你提到的代码片段:
using ThingName = std::string; using Error = std::string; std::vector<Thing> make_things(std::expected<std::vector<ThingName>, Error> names) { ... }
开发者意图猜测
- 大概率是想简化调用方代码:让调用方不用提前处理
expected的错误分支,直接把结果传给make_things,由它兜底处理错误。 - 也可能是对
std::expected的设计目的理解偏差——std::expected本质是用来传递函数执行的成功/失败结果,而非作为“带错误的输入参数”使用。
函数的实际契约
从代码实现(收到错误就抛异常)来看,这个函数的契约是:
我需要一个合法的
ThingName列表来生成Thing集合;如果传入的是错误信息,我会抛出异常终止当前流程,不会返回有效结果。
但这个契约和std::expected的设计初衷冲突——std::expected本就是为了避免隐式异常,让错误处理显式化,这种写法相当于绕回了异常式错误处理的老路。
是不是反模式?
没错,这属于典型的反模式,原因如下:
- 浪费了
std::expected的核心价值:它的存在就是为了让调用方显式选择错误处理方式(比如降级处理、重试、返回默认值),而非把错误隐式转成异常。 - 违背“错误尽早处理”原则:错误应该在产生的上游就被处理,而非传到下游核心逻辑里再崩溃。如果
make_things必须依赖合法输入,参数就该直接用std::vector<ThingName>,让调用方保证输入合法,或者在调用前处理错误。
有没有合理的使用场景?
几乎没有,但硬要找的话,可能存在两种过渡性场景:
- 逻辑错误兜底:如果上游的
expected错误本就属于“不可能发生”的逻辑错误(比如已经在其他地方做了严格校验),此时make_things收到错误就抛异常,相当于断言,用来捕获程序逻辑漏洞。 - 兼容旧代码:如果项目正在从异常式错误处理迁移到
std::expected,这个函数作为中间层,把expected的错误转成异常适配原有依赖异常的代码流程,但这只是临时方案,最终还是要统一错误处理风格。
内容的提问来源于stack exchange,提问作者Eugene K.
相关产品推荐
相关产品推荐

