在研究最近的一个问题时,我在'03标准[1]中找到了以下条款:
将typeid应用于左值时 表达式,其类型是多态的 类类型(10.3),结果参考 到表示的type_info对象 最派生对象的类型(1.8) (也就是动态类型) 左值是指。如果是左值 表达式是通过应用 指针的一元*运算符和 指针是空指针值 (4.10),typeid表达式抛出 bad_typeid异常(18.5.3)。
具体来说,我想知道最后一位,它为解除引用空指针的结果提供了良好定义的行为。据我所知,这是唯一一次这样做[2]。具体来说,dynamic_cast<T&>
对此案例没有特殊处理,这似乎是一个更有用的场景。因此,考虑dynamic_cast<T&>
已被定义为在某些情况下抛出异常。
是否有特定原因使这种特殊表达得到特殊处理?它看起来完全是武断的,所以我猜它们有一些具体的用例。
[1]'11中存在类似的子句,但它引用了glvalue表达式,而不是左值表达式。
[2] delete 0;
和dynamic_cast<T*>(0)
接近,但在这两种情况下,您都在处理指针值,而不是实际对象。
答案 0 :(得分:4)
如果我更加关注下一个条款(5.2.8 / 3),我会看到这个
当typeid应用于 除了左值之外的表达式 多态类型,。 。 。表达是 没有评估。
换句话说,与sizeof
一样(在C ++ 11中),编译器并不是要实际运行传递给typeid
的代码,它只是应该分析它为了行为。不幸的是,与sizeof
不同,由于多态类型,结果有时取决于表达式的运行时行为。
Base* p1 = new Derived;
Base* p2 = new Base;
typeid(*p1); //equivalent to typeid(Derived) [assuming Base is polymorphic]
typeid(*p2); //equivalent to typeid(Base)
如果表达式完全未被评估,则编译器无法检查RTTI以查看p1
实际上是指向Derived
而不是Base
。然而,标准编写者决定更进一步,并声明如果表达式最终是指针类型的解引用,编译器应该只对其进行部分计算。如果指针为null,则抛出std::bad_typeid
而不是执行取消引用并引入未定义的行为。
与dynamic_cast
对比。传递给dynamic_cast
的表达式总是被完全评估,否则结果就没有意义了。由于编译器无论如何都需要完全评估表达式,因此指示它提前停止并抛出异常是没有意义的。
简而言之,对此进行特殊处理的方式与sizeof(*(int*)0)
给予特殊处理的方式大致相同。 *(int*)0
并不打算进行评估,所以没有理由首先引入未定义的行为,即使它看起来很糟糕。