抛出静态类型的理由

Rationale for throwing static type?

本文关键字:理由 类型 静态类 静态      更新时间:2023-10-16

根据c++ FAQ,当抛出一个对象时,它是使用表达式的静态类型抛出的。因此,如果您有:

catch ( some_exception const &e ) {
  // ...
  throw e; // throws static type, possibly causing "slicing"; should just "throw;" instead
}

e 实际上是对源自some_exception的某个类的引用,上面的throw将导致对象被静默地"切片"。是的,我知道正确的答案是简单的throw;,但是事情的方式似乎是一个不必要的混乱和bug的来源。

这样做的理由是什么?为什么不希望通过对象的动态类型抛出呢?

当您执行throw时,从throw的操作数构建临时对象,该临时对象是被捕获的对象。

c++没有内置支持基于表达式的动态类型复制内容或创建对象,因此临时对象是操作数的静态类型。

throw的"参数"是一个表达式,它是表达式的类型,决定抛出的异常对象的类型。抛出的表达式的类型不一定是多态类型,因此可能没有办法确定表达式是否实际上引用了派生类型的基类子对象。

更简单的"表达式类型"规则还意味着实现不必在运行时动态确定异常对象的大小和类型,这可能需要为异常处理生成更复杂和更低效的代码。如果它必须这样做,它将表示语言中唯一需要调用点未知类型的复制构造函数的地方。这可能会大大增加实现成本。

考虑可以有对对象的引用,其中引用的静态类型是可复制的,但对象的动态类型不是。

struct foo {};
struct ncfoo : foo
{
private:
    ncfoo(ncfoo const&) {}
};
ncfoo g_ncfoo;
void fun()
{
    foo& ref = g_ncfoo;
    throw ref; // what should be thrown here?
}

如果你说"in this case just throw the static type",那么确切的规则是什么——"in this case"是什么意思?我们刚刚捕获的引用在没有复制的情况下被"重新抛出",其他所有内容都被复制?嗯…

但是无论你如何定义规则,它仍然会令人困惑。根据引用抛出会导致不同的行为,这取决于我们从哪里获得引用。Neh。c++已经足够复杂和令人困惑了:)