News Hacker|极客洞察

🤦C++ 异常与错误处理之争:std::expected、RAII 还是语法问题?
你真以为 Bjarne 能单独把 C ++修好吗?

🎯 讨论背景

原帖以“Bjarne fix your freaking language”为开端,引发对 C++ 异常机制与语言设计的讨论。评论围绕 exceptions 的正确用法、异常安全(在 open/close 间抛异常会导致资源泄漏)、RAII(构造/析构管理资源)以及用显式返回值(std::expected/Result)或单子替代异常展开。有人指出 C++ 的演进受 200 多人的委员会影响,Bjarne Stroustrup 已半退休,语言的问题不能简单归咎一人;讨论还把视野拓展到 Java、Rust、Swift、Go 和函数式语言的错误处理传统。部分评论给出具体代码示例或语法替代建议(如 ML 风格的 except/unless 或构造后检查句柄),并讨论实际工程中如何平衡可读性与异常安全。

📌 讨论焦点

误解异常与建议用 std::expected/单子式错误处理

多位评论指出原文对 exceptions 的用法存在误解,并错误地全面抨击异常机制。有人建议若目的是不让错误自动传播,应采用单子式或显式返回错误的方式,例如标准中的 std::expected,而不是滥用 try/catch。具体可行的做法包括用 RAII 管理资源并在构造后手动检查文件句柄(示例中的 File_handle),保证析构时释放资源同时让错误以返回值形式显式处理。评论还强调 C++ 的演进由 200+ 人的委员会决定,不能简单地指望 Bjarne 一个人去“修复”语言。

[来源1] [来源2]

try/catch 语法局限与替代提案

有人把问题聚焦到 try/catch 的语法本身,认为其当前语义会把错误处理包裹到一大块代码外,导致资源使用与异常处理难以局部区分。评论提出替代语法的设想(类 ML 风格的例子:try (File_handle fh {s,"r"}) { … } unless (const File_error& e) { … }),目的是让资源使用段不被异常处理块覆盖,并引用微软研究中的相关工作。也有更激进的观点直接建议不要使用 try/catch,而改用显式错误返回或单子来控制流,从根本上避免异常语义带来的复杂性。

[来源1] [来源2]

对 C++ 整体复杂性的批评与实务困境

部分评论将争论上升为对 C++ 语言整体复杂性的批评,引用“C++ FQA”作为对语言设计混乱或历史包袱的佐证。实务层面上,异常安全是反复出现的问题:在 open 与 close 之间任一点抛出异常都可能导致资源泄漏,必须依赖 RAII、额外检查或形式化校验来保证。有人因此放弃 C++,转而使用 C 加模型检查以简化资源管理并保证函数合约;反对者则认为很多问题来自范式混用或不恰当的用法而非语言本身,但普遍承认学习和维护成本很高。

[来源1] [来源2] [来源3] [来源4]

跨语言比较:Java、Rust、Swift、Go 与函数式的错误处理模式

评论中广泛比较了不同语言的错误处理策略:有人把 Java 的受检异常与 Rust 的 Result 并列,认为把异常视为方法契约能提高可靠性;但实际上 Rust 用 Result 而非异常传播错误。Swift 的 throws 被描述为通过特殊返回路径返回 Error 并强制处理,但不能声明异常类型;Go 则普遍用 if err != nil 返回错误,panic 的栈追踪与其他语言有差异。另有评论追溯 Result/Expected 的思想到函数式语言(如 Haskell 的 Either、F# 的 Result),指出函数式范式常为错误处理设计提供先导思想。

[来源1] [来源2] [来源3] [来源4] [来源5]

📚 术语解释

RAII: RAII(Resource Acquisition Is Initialization)——C++ 常用的资源管理模式,通过构造函数获取资源、析构函数释放资源,能在异常发生时自动保证资源释放,从而提高异常安全。

std::expected: std::expected(C++ 标准库类型)——类似于 Result ,用于封装成功值或错误,允许以返回值而非抛出异常的方式传播错误,便于显式检查和链式处理。

Result: Result (Rust/函数式编程中的结果类型)——包含 Ok(T) 或 Err(E),强制调用方显式处理错误,避免异常控制流带来的隐式传播。

monad / monads: monad(单子)——函数式编程中的抽象,用来封装计算上下文(例如可能失败的计算或可选值),支持链式组合操作,Either/Result 常被用作错误处理的单子实例。

try/catch: try/catch(异常捕获语法)——用来捕获和处理抛出的异常,讨论集中在其作用域、控制流语义与资源管理交互会导致异常安全问题,部分评论建议改进语法或避免使用。