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.