为什么标准不允许通过引用捕获不完整的异常类型?

Why is catching incomplete exception types by reference disallowed by the standard?

本文关键字:异常 类型 不允许 标准 引用 为什么      更新时间:2023-10-16

来自 C++17 标准(草案(,18.3.1:

[...]例外声明不应表示指向不完整类型的指针或引用 [...]

不允许通过引用捕获不完整类型背后的原因是什么?

如果函数参数是通过引用传递的,则仅当函数实际访问收到的对象时才需要完整类型,但它可以愉快地将参数传递给另一个函数,而无需了解该类型。

我不明白为什么这应该与异常不同 - 异常数据本身可能驻留在任何适当的位置,并且在我们找到引用的(展开到处理程序(堆栈上。好。如果处理程序现在可以仅从异常类型中拖动足够的信息,为什么它需要了解完整的定义?

那么我错过了什么?

struct Alice;
struct Bob;
int main() {
try {
throwAlice(); // extern
} catch (Bob&) {
return 0;
}
return 1;
}

以下程序返回什么? 0 或 1 还是鼻魔?好吧,这取决于Alice继承Bob.

若要处理异常捕获机制,编译器必须在编译时具有该信息。Bob应该是一个完整的类型。

原因解释在:

[except.handle]/15

由异常声明声明的变量,类型cv Tcv T&[YSC: 这里,cv T& = Bob&],从类型 E [YSC: 这里,E = Alice] 的异常对象初始化,如下所示:

  • 如果TE的基类,则变量从异常对象的相应基类子对象进行复制初始化;
  • 否则,变量将从异常对象进行复制初始化

这确认编译器需要在编译时知道Bob是否继承自AliceBob必须是完整的类型。

根据 VTT 对该问题的评论(不幸的是,在此期间被删除了"依赖于 RTTI 的异常处理"(以及从 YSC 的回答中得出的基本思想:

异常在运行时引发,异常对象放置在某个(在此范围内,但(明确定义的位置。

如果现在要捕获异常,我们还需要在运行时确定是否有任何异常处理程序与当前抛出的异常匹配,即像这样的代码:

try { throw e; }
catch(E1&) { /*...*/ }
catch(E2&) { /*...*/ }
catch(E3&) { /*...*/ }

必须在代码中产生"somehwere",如下所示:

if(e instanceof(E1)) { /*...*/ }
else if(e instanceof(E2)) { /*...*/ }
else if(e instanceof(E3)) { /*...*/ }

有一些适当的instanceof定义(从Java中窃取的术语...这样的定义现在需要比较E<x>的RTTI是否与e的RTTI匹配,或者e的任何基类之一;当然,对于这样的比较,E<x>的RTTI必须在编译时可用,因此E<x>的完整定义。

旁注:如果现在 RTTI 在某种泛型列表中包含基类的 RTTI,那么在编译时甚至不再需要知道抛出的异常的确切类型。