C++ typeid on Dereferenced Null Pointer
When you apply the C++ typeid operator to a dereferenced
null pointer of a polymorphic type, the expression does not evaluate to
a std::type_info object. Instead, the C++ runtime evaluates
the pointer expression and immediately throws a
std::bad_typeid standard exception. This behavior is
defined by the ISO C++ standard to prevent undefined behavior caused by
reading the dynamic type information from a non-existent object.
The Mechanism Behind the Exception
In C++, Run-Time Type Information (RTTI) allows a program to determine the exact dynamic type of an object at runtime. For a class type to be polymorphic, it must declare or inherit at least one virtual function.
When typeid is applied to an lvalue expression of a
polymorphic type, the compiler must evaluate the expression at runtime
to inspect the object's dynamic type (typically accessed via a pointer
to virtual table metadata). If the operand is produced by dereferencing
a pointer—such as typeid(*ptr)—the runtime checks whether
ptr is a null pointer. If ptr is null,
attempting to inspect its metadata would lead to a segmentation fault or
undefined behavior; therefore, the runtime safely interrupts execution
by throwing std::bad_typeid.
#include <iostream>
#include <typeinfo>
struct Base {
virtual ~Base() = default; // Makes Base polymorphic
};
int main() {
Base* ptr = nullptr;
try {
std::cout << typeid(*ptr).name() << '\n';
} catch (const std::bad_typeid& e) {
std::cout << "Caught: " << e.what() << '\n';
}
return 0;
}Contrast with Non-Polymorphic Types
The requirement to throw std::bad_typeid applies
exclusively to polymorphic types. If the expression represents a
non-polymorphic type, typeid is an unevaluated operand
resolved entirely at compile time.
For example, if ptr is a null pointer of type
int* or a pointer to a struct with no virtual functions,
typeid(*ptr) simply returns the static
std::type_info for that static type without evaluating the
dereference operation at runtime. In that scenario, no exception is
thrown, and no null-pointer dereference takes place.