What Is std::bad_typeid in C++ and When Is It Thrown?

In the C++ programming language, std::bad_typeid is a standard runtime exception defined in the <typeinfo> header that signals an invalid use of the typeid operator. This article explains the definition of std::bad_typeid, details the exact conditions required for the C++ runtime to throw it, contrasts its behavior with non-polymorphic types, and provides a clear code example demonstrating how it is triggered and handled.

What Is std::bad_typeid?

std::bad_typeid is a standard exception class derived directly from std::exception. It is part of the Run-Time Type Information (RTTI) mechanism in C++. When dynamic type identification cannot be safely performed because an object reference does not actually exist at runtime, the implementation throws this exception to prevent undefined memory access.

Conditions Under Which std::bad_typeid Is Thrown

The C++ runtime throws std::bad_typeid under a very specific condition:

  1. The typeid operator is applied to a dereferenced pointer: The expression takes the form typeid(*ptr).
  2. The pointer is null: The pointer value evaluates to nullptr (or 0).
  3. The type is polymorphic: The static type of the pointer points to a polymorphic class—meaning the class declares or inherits at least one virtual function.

Because the class is polymorphic, C++ must determine the actual dynamic type of the object at runtime by inspecting its virtual method table (vtable) pointer. When the runtime attempts to inspect *ptr and discovers that ptr is null, it cannot read the type metadata. Rather than causing undefined behavior or a direct segmentation fault, the runtime throws std::bad_typeid.

Behavior with Non-Polymorphic Types

If the class or type is non-polymorphic (it contains no virtual functions), typeid is evaluated entirely at compile time based on the static type of the expression. In that scenario:

  • Writing typeid(*ptr) on a null pointer to a non-polymorphic type does not evaluate the pointer at runtime.
  • The runtime check never executes.
  • std::bad_typeid is never thrown for non-polymorphic types.

Code Example

The following example demonstrates how dereferencing a null pointer to a polymorphic type triggers std::bad_typeid:

#include <iostream>
#include <typeinfo>

struct PolymorphicBase {
    virtual ~PolymorphicBase() = default; // Makes the class polymorphic
};

int main() {
    PolymorphicBase* ptr = nullptr;

    try {
        // Attempting to evaluate dynamic type of a dereferenced null pointer
        std::cout << typeid(*ptr).name() << std::endl;
    } catch (const std::bad_typeid& e) {
        std::cerr << "Caught std::bad_typeid: " << e.what() << std::endl;
    }

    return 0;
}

Prevention and Best Practices

To avoid encountering std::bad_typeid, ensure that any polymorphic pointer is verified to be non-null before dereferencing it inside a typeid expression:

if (ptr != nullptr) {
    std::cout << typeid(*ptr).name() << '\n';
}

When handling generic or dynamically loaded types where null states cannot be statically ruled out, wrap the typeid evaluation in a try...catch (const std::bad_typeid&) block.