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?
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.