While researching a recent question, I came upon the following clause in the '03 standard[1]:
When typeid is applied to an lvalue expression whose type is a polymorphic class type (10.3), the result refers to a type_info object representing the type of the most derived object (1.8) (that is, the dynamic type) to which the lvalue refers. If the lvalue expression is obtained by applying the unary * operator to a pointer and the pointer is a null pointer value (4.10), the typeid expression throws the bad_typeid exception (18.5.3).
Specifically, I am wondering about the last bit, which provides well defined behavior for the result of dereferencing a null pointer. As far as I can tell, this is the only time this is done[2]. Specifically, dynamic_cast<T&>
has no special treatment for this case, and that seems like a much more useful scenario. Doubly so considering dynamic_cast<T&>
is already defined as throwing an exception under certain circumstances.
Is there a specific reason that this particular expression was given special treatment? It seems completely arbitrary, so I am guessing there is some specific use case they had in mind.
[1] A similar clause exists in '11, but it refers to glvalue expressions, rather than lvalue expressions.
[2] delete 0;
and dynamic_cast<T*>(0)
come close, but in both cases you are dealing with a pointer value, rather than an actual object.
Null dereferencing Because a null pointer does not point to a meaningful object, an attempt to dereference (i.e., access the data stored at that memory location) a null pointer usually (but not always) causes a run-time error or immediate program crash. In C, dereferencing a null pointer is undefined behavior.
Dereferencing a null pointer always results in undefined behavior and can cause crashes. If the compiler finds a pointer dereference, it treats that pointer as nonnull. As a result, the optimizer may remove null equality checks for dereferenced pointers.
According to ISO C++, dereferencing a null pointer is undefined behaviour.
A NULL pointer dereference occurs when the application dereferences a pointer that it expects to be valid, but is NULL, typically causing a crash or exit. Extended Description. NULL pointer dereference issues can occur through a number of flaws, including race conditions, and simple programming omissions.
Had I paid more attention to the very next clause (5.2.8/3), I would have seen this
When typeid is applied to an expression other than an lvalue of a polymorphic class type, . . . The expression is not evaluated.
In other words, as with sizeof
(among others in C++11), the compiler is not meant to actually run the code you pass to typeid
, it is just supposed to analyze it for behavior. Unfortunately, unlike sizeof
, the result sometimes depends on the runtime behavior of the expression due to polymorphic types.
Base* p1 = new Derived;
Base* p2 = new Base;
typeid(*p1); //equivalent to typeid(Derived) [assuming Base is polymorphic]
typeid(*p2); //equivalent to typeid(Base)
If the expression were completely unevaluated, the compiler could not check the RTTI to see that p1
is actually pointing to a Derived
instead of a Base
. The standard writers decided to go one step further, however, and stated that if the expression is ultimately a dereference of a pointer type, the compiler should only partially evaluate it. If the pointer is null, throw std::bad_typeid
rather than perform the dereference and introduce undefined behavior.
Contrast that with dynamic_cast
. The expression passed to dynamic_cast
is always fully evaluated, the result would make no sense otherwise. Since the compiler is required to fully evaluate the expression anyhow, it makes no sense to instruct it to stop early and throw an exception.
In short, this is given special treatment in much the same way that sizeof(*(int*)0)
is given special treatment. *(int*)0
isn't meant to be evaluated, so there is no reason to introduce undefined behavior in the first place, even though it looks bad.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With