取消引用typeid中的空指针

Den*_*ose 16 c++ typeid

在研究最近的一个问题时,我发现了'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)接近,但在这两种情况下,您都在处理指针值,而不是实际对象.

Den*_*ose 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)
Run Code Online (Sandbox Code Playgroud)

如果完全不对表达式求值,则编译器无法检查 RTTI 以查看它p1实际上是指向 aDerived而不是 a Base。然而,标准编写者决定更进一步,并指出如果表达式最终是指针类型的取消引用,则编译器应该只部分评估它。如果指针为空,则抛出std::bad_typeid而不是执行取消引用并引入未定义的行为。

与 对比一下dynamic_cast。传递给的表达式dynamic_cast总是被完全求值,否则结果将毫无意义。由于无论如何都需要编译器完全评估表达式,因此指示它提前停止并抛出异常是没有意义的。

简而言之,这与给予特殊待遇的方式大致相同sizeof(*(int*)0)*(int*)0并不意味着要进行评估,因此没有理由首先引入未定义的行为,即使它看起来很糟糕。