带有typeid的逗号运算符

Rak*_*kib 15 c++ dynamic-cast comma typeid

我正在研究一种通用的非侵入式智能指针实现.我在第4节中有些困惑.一个声明是

仅当结果类型是多态类类型的左值时,才计算作为typeid运算符的参数提供的表达式.

相关的示例代码是:

template<typename T>
  void* startOfObject(T* p) {
  void* q=static_cast<void*>(p);
  typeid(q=dynamic_cast<void*>(p),*p); // This line
  return q;
}
Run Code Online (Sandbox Code Playgroud)

AFAIU,它意味着q=dynamic_cast<void*>(p)如果结果类型多态类类型左值,则将对其进行评估.该结果意味着评价的结果dynamic_cast<void*>(p)(我想),所以dynamic_cast在任何情况下被应用.文章陈述(据我所知),如果p不是多态的dynamic_cast就不会应用,但为什么呢?在应用它之前,如何知道结果是否是多态的?如果有人详细描述了如何执行完整的语句,将会很有帮助.

另一种说法是

如果p为NULL也存在问题 - typeid将抛出std :: bad cast.

我看到的问题是取消引用,如果pNULL,而不是typeid(虽然它可能抛出bad_typeid,但这不是因为转换).dynamic_cast会返回一个NULL类型的指针void*,如果pNULL,而且typeid应该能够推断出类型的信息.这是一个错字,还是我错过了什么?

MSa*_*ers 11

基本上编写以下代码是一个奇特的技巧

if (T is polymorphic)
  return dynamic_cast<void*>(p);
else
  return static_cast<void*>(p);
Run Code Online (Sandbox Code Playgroud)

使用的技巧是以typeid(expr)两种方式之一进行评估.如果编译器确定expr具有非多态类型,则继续使用其静态类型.但是如果expr有动态类型,它会expr在运行时进行评估.因此,当且仅当*p逗号是多态的时,才会对逗号运算符之前的赋值进行求值.

由于这个原因,null情况很复杂.如果T不是多态的,那么typeid(*p)在编译时由编译器替换,并且运行时空指针根本不重要.如果T是多态的,则应用空指针解引用的特殊处理,并且该特殊处理表明std::bad_typeid抛出了异常.


jas*_*sal 5

逗号运算符具有从左到右的关联性,并从左到右进行求值.这意味着表达式的结果q=dynamic_cast<void*>(p),*p*p.因此,仅当类型*p为多态时才评估动态强制转换.

关于NULL问题标准规定:

当typeid应用于类型为多态类类型(10.3)的左值表达式时,结果引用一个type_info对象,表示左值引用的最派生对象(1.8)的类型(即动态类型) .如果通过施加一元运算符*的指针所得到的左值表达式和指针是空指针值(4.10)时,typeid的表达抛出bad_typeid异常(18.5.3).


Fil*_*efp 5

说明

由于C++是一种静态类型语言,因此即使在涉及多态行为的情况下,每个表达式的类型在编译时也是已知的.

在编译时已知某个类是否是多态的,在程序执行之间将不会更改此属性.

如果编译器看到的EXPRtypeid(expr)不会产生多态类类型的值,它只会把EXPR "未计算",这相当于在运行时不执行它.


!!!

重要的是要注意expr必须仍然有效,我们不能typeid用来潜在地忽略一个不正确的表达式; 如果表达式格式错误,仍必须发出编译器诊断.

仅仅因为expr将被"未评估"并不意味着我们可以让它包含一个格式错误的子表达式.

struct A { }; // not polymorphic

...

A * ptr = ...;
typeid (dynamic_cast<void*> (ptr), *ptr); // ill-formed
Run Code Online (Sandbox Code Playgroud)

因为ptr不是指向多态类的指针,所以我们不能在dynamic_cast<T>其中使用它T = void*,如中所述[expr.dynamic.cast]p6.


取消引用潜在的nullptr

这将使typeidthrow成为异常,因此它不会产生未定义的行为,但如果使用此类实现,则应准备好处理异常.

5.2.8p2 类型识别 [expr.typeid]

(...)如果通过将一元运算*符应用于指针并且指针是空指针值(4.10)来获得glvalue表达式,则typeid表达式将抛出与类型处理程序匹配的类型的异常(15.1)std::bad_typeid例外(18.8.3)


澄清

这意味着如果结果类型是多态类类型的左值,则将评估q = dynamic_cast(p).结果意味着评估dynamic_cast(p)的结果(我猜).

不,作者所说的是,如果整个表达式的结果类型是多态类类型,那么将对表达式进行求值.

注意:"整个表达式"指的是...in typeid(...),在您的情况下; q=dynamic_cast<void*>(p),*p.


逗号运算符

逗号运算符将采用两个操作数expr1expr2,它将通过计算expr1开始,然后丢弃该值,之后它将计算expr2并产生该表达式的值.


把它放在一起

这意味着使用逗号运算符的结果类型就好像它只包含右侧表达式,并且在下一行中,编译器将检查以便expr2的结果是一个多态类的类型.

typeid (expr1, expr2)
Run Code Online (Sandbox Code Playgroud)

typeid (q=dynamic_cast<void*>(p), *p)

// expr1 = q=dynamic_cast<void*>(p)
// expr2 = *p
Run Code Online (Sandbox Code Playgroud)

注意:在C++ 03中,结果类型必须是多态左值,在C++ 11中,这已被更改为多态类类型的glvalues.