C++ 取消对typeid中的空指针的引用

C++ 取消对typeid中的空指针的引用,c++,typeid,C++,Typeid,在研究最近的一个问题时,我在'03标准[1]中发现了以下条款: 当typeid应用于左值时 类型为多态的表达式 类别类型(10.3),结果参考 指向表示 最派生对象的类型(1.8) (即,动态类型)将 左值是指。如果左值 表达式是通过应用 指向指针的一元*运算符和 指针是空指针值 (4.10),typeid表达式抛出 bad_typeid异常(18.5.3) 具体来说,我想知道最后一位,它为解引用空指针的结果提供了定义良好的行为。据我所知,这是唯一一次这样做[2]。具体来说,dynamic\u

在研究最近的一个问题时,我在'03标准[1]中发现了以下条款:

当typeid应用于左值时 类型为多态的表达式 类别类型(10.3),结果参考 指向表示 最派生对象的类型(1.8) (即,动态类型)将 左值是指。如果左值 表达式是通过应用 指向指针的一元*运算符和 指针是空指针值 (4.10),typeid表达式抛出 bad_typeid异常(18.5.3)

具体来说,我想知道最后一位,它为解引用空指针的结果提供了定义良好的行为。据我所知,这是唯一一次这样做[2]。具体来说,
dynamic\u cast
对这种情况没有特殊处理,这似乎是一个更有用的场景。更重要的是,考虑到
dynamic_cast
已经被定义为在某些情况下抛出异常

有没有一个特定的原因,这个特殊的表达被给予特殊的待遇?这似乎完全是武断的,所以我猜他们心里有一些特定的用例


[1] '11中存在类似的子句,但它引用的是glvalue表达式,而不是左值表达式


[2]
删除0
dynamic\u cast(0)
非常接近,但在这两种情况下,您处理的都是指针值,而不是实际对象。

如果我更加注意下一个条款(5.2.8/3),我会看到这一点

当typeid应用于 除a的左值以外的表达式 多态类类型。表达式是 未评估

换句话说,与
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
是否实际指向
派生的
,而不是
基的
。然而,标准编写人员决定更进一步,并指出,如果表达式最终是指针类型的解引用,编译器应该只对其进行部分计算。如果指针为null,则抛出
std::bad_typeid
,而不是执行取消引用并引入未定义的行为

dynamic\u cast
进行对比。传递给
dynamic\u cast
的表达式始终是完全求值的,否则结果将毫无意义。因为编译器无论如何都需要完全计算表达式,所以指示它提前停止并抛出异常是没有意义的


简言之,这与对
sizeof(*(int*)0)
进行特殊处理的方式大致相同
*(int*)0
不是用来计算的,因此没有理由首先引入未定义的行为,即使它看起来很糟糕。

动态\u cast
的情况下,有这样的规定:5.2.7/4如果v的值在指针情况下是空指针值,结果是R类型的空指针值。至于为什么它不抛出,那么,
dynamic_cast
有两个版本,在指针上它不会抛出(而是返回0),而对于引用,如果它不能执行转换,它将抛出。@David:我想我误读了上一节;我认为这是
T
v
是同一类型的情况的扩展。回头看,你可能是对的。然而,对于参考案例,它仍然没有定义。正如标准的其余部分所预期的那样。@Dennis Zickefoose:在有效的程序中不可能有空引用。获取无效引用的唯一方法是未定义的行为,因此不需要特别规定UB已经发生后程序中可能发生的情况
typeid
sizeof
一样特殊,它们不是常规函数或强制转换。在
dynamic\u cast(*p)
的情况下,您正在评估
*p
,然后在参考上应用
dynamic\u cast
。在
typeid
sizeof
的情况下,您询问的是表达式,而不是值。除非表达式是非空指针的解引用,否则不会对其求值,因为如果类型可以由编译器静态推断(非多态指针、函数调用…),则永远不会对表达式求值。问题是:“为什么
dynamic\u cast
不以这种方式工作?”答案是,当您对指针调用
dynamic\u cast
时,它将在取消引用之前检查null以获得动态类型。问题是,如果您取消引用该参数,则该操作将在到达
dynamic\U cast
之前进行计算,即:
dynamic\U cast(*(U*)0)
相当于
U&U=*(U*)0;动态铸型(u),未定义的行为是在计算参数时发生的。是否有标准中支持此操作的引用?@Xeo:是的,但我手边没有副本,所以他们必须等到早上。我打算把它充实起来,我只是想把它从“未回答”的列表中去掉,因为大卫在评论中做了所有繁重的工作。好吧,我在等它。:)此外,总是有C++0x FDI可以快速浏览。@Xeo:好了。在C++11的同一部分中,对于左值的扩展类型以及所有未赋值表达式的新类型,措辞略有不同,但jist是相同的。您始终可以使用函数调用作为
typeid
的参数。如果函数被声明为返回左值