【问题标题】:In C++, Is it possible to force the user to catch exceptions?在 C++ 中,是否可以强制用户捕获异常?
【发布时间】:2016-02-09 17:19:52
【问题描述】:

简而言之,是否有可能让 C++ 强制方法的调用者放置一个 try...catch 块?

(澄清: 我不一定指直接调用者,我的意思是强迫它在某处被捕获。另外,我说的是在编译时强制。)

长:

我读过它不建议使用异常规范,并且无论如何它都不能正常工作(http://4thmouse.com/mystuff/articles/UsingExceptionsEffectively.html

但普遍的共识似乎倾向于使用异常来返回错误,而不是用户编写返回错误代码的方法。

如果我正在编写一个库,那么如何阻止用户调用我的方法而不放置任何 try...catch 块,然后在我的代码抛出异常时让他的程序崩溃?

(需要明确的是,我只需要在用户堆栈中的某处捕获异常,不一定在立即调用代码中,如果不是这种情况,编译器会抱怨。)

【问题讨论】:

  • 什么都没有。这就是为什么会有例外。您可以选择抓住它们或让它们“上楼”。如果根本没有捕捉到它们,那么操作系统会捕捉到它并且您的应用会关闭。
  • 当您阅读“普遍共识”时,将其视为“法律”。返回错误代码而不是异常的库是一个真正的苦差事。 :)
  • 问题是,你为什么在乎?这是他们的程序崩溃了,所以这是他们的问题。
  • @Angew,想象一下我们和他们共同负责一个项目。 :-)
  • @nappyfalcon:你的意思是每个人都讨厌使用异常规范的语言,因为他们必须使用异常规范?

标签: c++ c++11 exception exception-handling


【解决方案1】:

不,不是。

确实,没有机制可以强制函数的调用者(调用堆栈中的任何位置)处理任何类型的错误。至少,不是通过编译失败。返回值可以被丢弃。如果用户在获取值之前没有实际检查该值是否可用,即使将错误代码与返回值捆绑在一起(通过expected<T, E>)也不会发出编译时错误。

C++17 可能会给我们[[nodiscard]] 属性,它允许编译器在调用者丢弃返回值(可能是错误代码)时发出警告。但是编译时警告将尽可能接近。

【讨论】:

  • WRT 你的最后一点,对不起,我的意思是,我可以强迫用户在他们的堆栈中的某个地方抓住它吗?我会更新问题。
  • @nappyfalcon 把它放在你的库接口会抛出异常的文档中。这就是我所期望的。我通常会放一个catch(std::exception const & e) 和一个catch( ... ) 与记录轮main()
  • @nappyfalcon:直接呼叫者或最终呼叫者,它不会改变答案。
  • 如果客户真的有意忽略错误,那么他们会想方设法这样做。无论是空的 catch 块还是 int unused_result = f();.
  • @ChristianHackl:最好使用auto _ = f();。更少的打字,它适用于任何事情。
【解决方案2】:

简而言之,是否有可能让 C++ 强制方法的调用者 放一个 try...catch 块?

没有。这将违背例外的全部目的。例外是专门针对跨多个层传播错误而中间层不知道它们的用例。

假设您有一个像 A -> B -> C -> D -> E 这样的调用层次结构,并且在 E 中发生错误。A 可以处理该错误。 B、C 和 D根本不需要意识到错误。这正是例外的好处!

如果您想直接向调用者返回错误,因为处理错误确实是调用者关心的问题,那么异常通常是错误的设计,返回值可能是更好的选择。


已经在 J​​ava 中尝试过某种形式的“强制”异常,但我认为这是一个失败的实验,因为它通常会导致如下代码:

try {
    method();
} catch (SomeCheckedException ex) {
    // ignore
}

C++ 不鼓励这样做应该被视为一个特性


我读过不建议使用异常规范和 无论如何它都不能正常工作

没错。唯一有用且有效的异常规范是 throw(),它表示根本没有抛出异常,并且在 C++11 中已被 noexcept 取代。

但普遍的共识似乎倾向于使用例外 在编写返回错误的方法的用户上返回错误 代码。

见上文。这取决于您是否希望传播错误,或者调用者是否可以并且应该处理它。

所以,如果我写的是一个库,那么如何阻止用户调用 我的方法没有放任何 try...catch 块,然后得到他的 当我的代码抛出异常时程序崩溃?

要求其用户用try 块包围所有函数调用的库的接口不好,因此应该重新设计。

另外...您假设“程序”将使用您的库。但这个假设并不总是正确的。库客户端可能本身是一个库。程序和你的库之间可能有很多不同的库层。如果您不在乎哪个层处理它们,则可以使用异常。

【讨论】:

    【解决方案3】:

    有一个普遍的共识?不是我知道的。至于例外,没有。编译器不能强制 somebody 在调用堆栈的某处捕获异常。在编译时,编译器不知道谁可能在调用您的函数,并且您的函数可能会抛出任意异常,就像您的函数调用的任何函数一样。链接器可能有机会,但它必须维护大量额外信息来处理函数可能抛出的异常以及函数可能捕获的异常。当您开始谈论动态加载的库 (DLL/.so) 时,这会变得更加丑陋,因为它必须在运行时解决。

    【讨论】:

      猜你喜欢
      • 2015-11-14
      • 1970-01-01
      • 1970-01-01
      • 2011-12-17
      • 1970-01-01
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      相关资源
      最近更新 更多