Search code examples
c++dynamic-casttypeidcomma-operator

Comma operator with typeid


I was studying A Generic Non-intrusive Smart Pointer Implementation. I have some confusion in section 4. One statement is

the expression supplied as the argument to the typeid operator is only evaluated if the result type is an lvalue of polymorphic class type.

And associated example code is:

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

AFAIU, it means q=dynamic_cast<void*>(p) will be evaluated if the result type is an lvalue of polymorphic class type. The result means the result of evaluating dynamic_cast<void*>(p) (I guess), so the dynamic_cast has to be applied in any case. The articles states (as I understand) that if p is not polymorphic then dynamic_cast will not be applied, but why? Before applying it, how can it be known whether the result is polymorphic or not? It will be helpful if someone describes in details how the full statement will be executed.

Another statement is

There is also a problem if p is NULL – the typeid will throw a std::bad cast.

The problem I see is with de-referencing if p is NULL, not with typeid (although it may throw bad_typeid, but that is not because of casting). dynamic_cast will return a NULL pointer of type void* if p is NULL, and typeid should be able to deduce the type information. Is that a typo, or am I missing something?


Solution

  • It's a fancy trick to write essentially the following code

    if (T is polymorphic)
      return dynamic_cast<void*>(p);
    else
      return static_cast<void*>(p);
    

    The trick used is that typeid(expr) is evaluated in one of two ways. If the compiler determines that expr has a non-polymorphic type, it goes ahead and uses its static type. But if expr has a dynamic type, it evaluates expr at runtime. The assignment before the comma operator is therefore evaluatad if and only if *p after the comma is polymorphic.

    The null case is complex for that reason. If T is not polymorphic, then typeid(*p) is replaced by the compiler at compile time, and the runtime null pointer doesn't matter at all. If T is polymorphic, special handling of null pointer dereferences applies, and that special handling states that a std::bad_typeid exception is thrown.