从C ++ 17标准(草案),18.3.1:
[...]异常声明不得表示指向不完整类型的指针或引用[...]
引用不允许捕获不完整类型的原因是什么?
如果函数参数是通过引用传递的,那么只有在函数实际访问接收到的对象时才需要完整类型,但它可以很高兴地将参数传递给另一个函数而不知道该类型的任何内容。
我不明白为什么这应该与异常不同 - 异常数据本身可能任何地方适当,并且在(unwinded up to the handler)堆栈中我们找到引用。精细。如果处理程序现在只能从异常类型中拖动足够的信息,为什么需要知道完整的定义呢?
那我错过了什么?
答案 0 :(得分:10)
struct Alice;
struct Bob;
int main() {
try {
throwAlice(); // extern
} catch (Bob&) {
return 0;
}
return 1;
}
以下程序返回什么? 0或1或鼻子恶魔?好吧,这取决于Alice
继承自Bob
。
要处理异常捕获机制,编译器必须在编译时获得该信息。 Bob
应该是完整的类型。
原因在于解释:
[except.handle]/15
exception-declaration 声明的类型
cv T
或cv T&
[YSC:here,cv T& = Bob&
]声明的变量是从异常对象初始化的,类型E [YSC:此处,E = Alice
],如下所示:
- 如果
T
是E
的基类,则该变量是 copy-initialized ,来自异常对象的相应基类子对象;- 否则,变量是 copy-initialized 来自异常对象。
确认编译器需要知道Bob
在编译时是否继承Alice
:Bob
必须是完整类型。
答案 1 :(得分:3)
根据VTT对问题的评论(同时不幸地删除了“依赖RTTI的异常处理”)和YSC的answer所得的基本思想:< / p>
在运行时抛出异常,异常对象放在某些位置(在此范围内unknwon,但是)定义良好的位置。
如果现在要捕获异常,我们需要在运行时确定以及,如果任何异常处理程序与当前抛出的异常匹配,i。即一些代码如下:
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
基类的RTTI;当然,对于这样的比较,E<x>
的RTTI必须在编译时可用,因此是E<x>
的完整定义。
旁注:如果现在RTTI在某种泛型列表中包含基类的RTTI,则在编译时甚至不再需要知道抛出的异常的确切类型。 / p>