为什么 C++ 允许抛出任何东西?

Why does C++ allow to throw anything?

刚刚看到及其相关的回答。 考虑到我以前从未 运行 以这种方式使用 throws,我很惊讶地发现这甚至是可能的。

1. 您可以抛出任何东西,因为 C++ 没有像 java 或 C# 那样定义特定 classes 的目的。在这种语言中,您只能抛出继承自指定异常 class 的 classes 的对象。 C++ 没有 the one 异常 class(没有人强迫你使用 std-stuff)。

2. 使用 throw/catch 表示非错误消息被称为异常驱动开发,确实被认为是不好的做法。异常驱动的开发导致代码难以理解。它还可能导致代码段无法无意中执行。我强烈建议不要这样做。

如果不推测这很难回答,但 Bjarne 1998 年的论文 "An Overview of the C++ Programming Language" 在描述异常处理时使用了任意类型,并建议为 convenient/semantics 创建所述类型的层次结构。看起来他一开始并没有想到 exception 的 "base class"。

拥有标准层次结构(基于 std::exception)的概念可能是作为 "addition" 开始的,这是一种处理 Bjarne 建议使用异常的便捷方法,而不是构建-每个人对异常的使用都应该基于这些块。当代实践是从 std::exception 中导出所有异常似乎是后来出现的。

现在,我想不出不这样做的好理由,如果没有其他原因,使用您的代码的人可能会期望顶级 catch (const std::exception&) 吸收任何有用的东西。不过,我也倾向于在 main 中放一个 catch (...),以防万一。

更实际地说,如果不是这种情况,则必须有额外的规则将 throw 限制为仅 "valid" 在类型源自 std::exception 的表达式上,这似乎没有任何现实世界的好处足以证明额外规则的合理性。不管你信不信,C++ 在问题 "why don't we have such-and-such a rule" 方面来自极简主义的地方,尽管这些年来它的膨胀显然与此相矛盾。

我怀疑这与将 throw 用于非特殊情况有什么关系,因为这一直被认为是不好的做法。 Bjarne 描述此功能的方式是:

Exceptions are used to transfer control from a place where an error is detected to some caller that hasexpressed interest in handling that kind of errors. Clearly, this is a mechanism that should be used only for errors that cannot be handled locally. [..] Exceptions can be used to make error handling more stylized and regular.

所以很明显,至少最初的设计意图(而且,这仍然是常识)是仅对 exceptional/errory 个案例使用例外。

C++ 的一般哲学是允许程序员做他们喜欢做的事(并搬起石头砸自己的脚),大多数限制只是为了实现可移植性,甚至有些编译器也没有强制执行.

如果您不需要(公认的小)创建 std::exception 对象的开销,C++ 不会强迫您为此付费。例如你可以直接抛出一个错误代码,这比创建一个带有错误代码成员的 std::exception 的子类要容易得多。当然,您的代码的调用者需要知道期望捕获数字而不是 std::exception 子类。由于这个原因,通常最好只抛出 std::exception 的子类,但你不必这样做。

我不是 Bjarne Stroustrup,但我会尽力给出答案。

What's the logic behind allowing (almost) anything to be thrown and caught?

惯例是从标准库中抛出 std::exception 的派生物。如果你限制可以抛出的东西,你就只能使用它。 C++ 试图: 1. 给你很大的灵活性 2.管理语言已经很复杂的复杂性 通过让语言抛出和捕捉任何东西,你给了程序员灵活性。通过将 std::exception 移动到标准库而不是将其作为语言本身的一部分,您还使语言变得不那么复杂。

Is there a use of the throw-catch syntax that goes beyond exception/error signaling?

是的,你可以用它来传递信号。这是一种不明智的反模式(滥用)。

When the handler is entered, e will be a copy of the Int_overflow object that was created inside the add function to describe what went wrong. The parallel to function calls is now almost exact: The throw-expression passes an Int_overflow object to the handler declared to accept objects of class Int_overflow. The usual initialization semantics are used to pass this argument. The type of the object thrown is used to select the handler in approximately the way the arguments of a function call is used to select an overloaded function. Again, a slightly different way of looking at the throw-expression is as a return statement where the argument is not only passed back to the caller, but is also used to select which caller to return to.
[...]
Relying of such fundamental and well-supported language concepts rather than inventing some special ‘‘exception type’’ ensures that the full power of C++ is available for the definition and use of exceptions.

"Exception Handling for C++",安德鲁·科尼格、比亚恩·斯特劳斯特鲁普,1989

这可能会回答您有关该行为背后逻辑的问题。 C++ 的创建者希望使用现有技术来实现新功能。

关于您关于合法使用该功能的问题,我将引用同一出版物中的另一句话:

We do not wish to use exception handling as a substitute for more conventional control structures

我留给你和其他评论者来决定是否有更多的现代实践可能已经超越了发明者最初的想法。