根据the C++ FAQ,当一个人抛出一个对象时,它会使用表达式的 static 类型抛出。因此,如果你有:
catch ( some_exception const &e ) {
// ...
throw e; // throws static type, possibly causing "slicing"; should just "throw;" instead
}
并且e
实际是对从some_exception
派生的某个类的引用,上面的throw
将导致对象被静默“切片”。是的,我知道正确答案仅仅是throw;
,但事情似乎是不必要的混淆和错误来源。
这是什么理由?为什么不希望通过对象的动态类型抛出它?
答案 0 :(得分:9)
当你throw
某事时,临时对象是从throw
的操作数构造的,而临时对象是被捕获的对象。
C ++没有内置支持根据表达式的动态类型复制内容或创建对象,因此临时对象属于操作数的静态类型。
答案 1 :(得分:4)
throw
的“参数”是一个表达式,它是表达式的类型,用于确定抛出的异常对象的类型。抛出的表达式的类型不一定必须是多态类型,因此可能无法确定表达式是否实际引用了更多派生类型的基类子对象。
更简单的“表达式类型”规则还意味着实现不必在运行时动态确定异常对象的大小和类型,这可能需要为异常处理生成更复杂和效率更低的代码。如果必须这样做,它将代表语言中唯一需要在调用点处未知类型的复制构造函数的地方。这可能会大大增加实施成本。
答案 2 :(得分:0)
考虑到我们可以引用可引用静态类型的对象,但对象的动态类型不是。
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?
}
如果你说“在这种情况下只是抛出静态类型”,那么具体规则是什么 - “在这种情况下”是什么意思?我们刚刚捕获的引用被“重新抛出”而没有复制,其他一切都被复制了?唔...
但是你定义规则,它仍然会令人困惑。按引用引发将导致不同的行为,具体取决于我们从哪里获得该引用。尼。 C ++已经很复杂,令人困惑:)