【问题标题】:Why is catching incomplete exception types by reference disallowed by the standard?为什么标准不允许通过引用捕获不完整的异常类型?
【发布时间】:2018-06-07 07:42:40
【问题描述】:

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

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

reference 不允许捕获不完整类型的原因是什么?

如果函数参数是通过引用传递的,那么只有在函数实际访问接收到的对象时才需要完整的类型,但它可以很高兴地将参数传递给另一个函数,而无需了解类型。

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

那么我错过了什么?

【问题讨论】:

    标签: c++ exception-handling language-lawyer


    【解决方案1】:
    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 Tcv T& [YSC:此处为 cv T& = Bob&],从异常对象初始化,类型为 E [YSC :这里,E = Alice],如下:

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

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

    【讨论】:

    • 我自己的答案失败了——即使instanceof 部分在技术上是正确的,RTTI 也可能会通过一些指针来使用——它可能会被编译为占位符(就像调用函数一样声明可用)并在链接器运行之前填写(即结论“需要RTTI - >需要完整类型”是错误的)......当前答案现在表明需要“爱丽丝”作为完整类型 - 但我仍然坚持空气中带有“为什么Bob”...
    • 复制初始化引用对我来说似乎没有意义,除非我将其解释为“复制异常对象的地址”(对于基类,当然是相应基对象的地址)。你会同意吗?为此,我只需要知道BobAlice 中的偏移量,但我仍然不需要Bob 的完整定义...
    • 我感觉就像站在马赛克前,看到整体图像,但缺少一些最小但重要的关键部分,无法理解这幅马赛克试图告诉我什么......跨度>
    • 你知道,有时标准会无缘无故地强加一些东西,比如 in C++98 you couldn't use a type defined in a function to instantiate a template,在 C++11 中已经放宽了,所以 lambda 是相关的。
    【解决方案2】:

    基于VTT对该问题的评论(同时不幸删除,“依赖RTTI的异常处理”)和源自YSCanswer的基本思想:

    在运行时抛出异常,异常对象被放置在某个(在此范围内未知,但)定义明确的位置。

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

    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 在某种 generic 列表中包含基类的 RTTI,那么在编译时甚至不需要知道抛出的异常的确切类型。

    【讨论】:

    • RTTI 很可能会通过地址(指针)提供给内存中某处的某个 RTTI 对象。类似于对仅预先声明的函数的调用,这些指针可能仅编译为占位符,并且在链接器运行之前不会填充实际地址。所以“RTTI 必须在编译时可用”的结论不一定正确!
    猜你喜欢
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-13
    • 1970-01-01
    • 2011-08-12
    相关资源
    最近更新 更多