rtti

static &dynamic type information

class Base {
public:
   virtual ~Base();
   virtual void name();
};
class Derived1 : public Base;
class Derived2 : public Base;
Base* pd1 = new Derived1;
Base* pd2 = new Derived2;
// static type Base
// dynamic type DerivedX
dynamic_cast

rtti

static type (compile time)

decltype
auto

dynamic type (runtime)

dynamic dispatch

using t = decltype(pd1); // Base*
auto p2 = pd1; // Base*

Base* p = new Base();
// no indication of error
static_cast<Derived1*>(b);
pd1()->name(); // Derived1::name
Base* bp = new Base();
dynamic_cast<Derived1*>(b); // nulp
assert(
    typeid(*pd1) == typeid(*pd2)
) // fail

static

dynamic

static_cast
typeid
#include <typeinfo>
typeid(whatever) -> const std::type_info&
typeid(type)
typeid(expr_of_polymorphic_t)
typeid(expr_of_non_polym_t)
typeid(size_t)
typeid(std::string("Hello"))
auto p = new PolymorphicT;
typeid(*p);

Compile time

Compile time

Runtime

typeid(*p)
PolymorphicT p{}; // 0 initialized
typeid(*p); // std::bad_typeid
where p == nullptr

Runtime

throw std::bad_typeid
no UB
  • of static storage duration
  • theoretically subsequent invocations on the same type could return different objects
std::type_info
namespace std {
    class type_info {
        type_info() = delete;
        type_info(type_info) = delete;
        type_info& operator=(type_info) = delete;
        virtual ~type_info();
        
        bool operator==(const type_info& rhs) const noexcept;
        // (operator!= synthesized since C++20)
        bool before(const type_info& rhs) const noexcept;
        std::size_t hash_code() const noexcept;
        const char* name() const noexcept;
    };
}
std::type_info
namespace std {
    class type_info {
        type_info() = delete;
        type_info(type_info) = delete;
        type_info& operator=(type_info) = delete;
        virtual ~type_info();
        
        bool operator==(const type_info& rhs) const noexcept;
        // (operator!= synthesized since C++20)
        bool before(const type_info& rhs) const noexcept;
        std::size_t hash_code() const noexcept;
        const char* name() const noexcept;
    };
}
void use(Base* p) {
    auto&& type = typeid(*p);
    if (type == typeid(Derived1)) {
        special_logic1();
    }
    else if (type == typeid(Derived2)) {
        special_logic2();
    }
    ...
}
std::unordered_map<std::type_index, std::string> m;
m[typeid(double)] = "Doubles are red";
m[typeid(32LL)] = "Long longs are blue";
m[typeid(std::string)] = "And this is cool";

for (auto&& [idx, msg] : m) {
    std::cout << idx.name() << ": " << msg << '\n';
}
std::type_info
std::unordered_map<std::type_index, std::string> m;
m[typeid(double)] = "Doubles are red";
m[typeid(32LL)] = "Long longs are blue";
m[typeid(std::string)] = "And this is cool";

for (auto&& [idx, msg] : m) {
    std::cout << idx.name() << ": " << msg << '\n';
}
#include <typeinfo>
#include <typeindex>

std::type_index(typeid(whatevah));

returns mangled name

getting names of types

types

std::type_info::name()

mangled name

readable name

typeid(std::string).name()
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >

example under g++

getting names of types

types

getting names of types

types

constexpr auto name = typeid(int).name();

into the detailverse

D1*

B1*

...
...
VFTablePtr
struct B1 {
    ...
    virtual void foo() {}
};
struct D1 : B1 { ... };
struct B2 {
    ...
    virtual void bar() {}
}
struct D2 : D1, B2 { ... }
...
...

D2*

B2*

VFTablePtr
top_offset = 0
typeinfo for D2
B1::foo
top_offset = -8
typeinfo for D2
B2::bar

D1

B1

D2

B2

...
...
mangled_name

typeinfo for D2

vtable for d2

d2

example under g++

Performance

Performance

Performance

Performance

under libstdc++ (g++) and libc++ (Clang)

Performance

Performance

Performance

Performance

std::type_info::operator==
std::type_info::hash_code
std::type_info::name
O(1)
O(length(name))
O(length(name))
O(1)

or

under libstdc++ (g++) and libc++ (Clang)

!defined(_LIBCPP_NONUNIQUE_RTTI_BIT)
__GXX_MERGED_TYPEINFO_NAMES == 0

libstdc++

otherwise

O(1)
O(length(name))

default

O(1)

otherwise

O(length(name))

libc++

default

non standard compliant

disabling rtti

-fno-rtti
/GR-

under G++, Clang and similar

under MSVC

inheritance and exceptions

class my_custom_error : public std::runtime_error {
private:
	std::string message;
public:
	my_custom_error(std::string msg) :
        message{std::move(msg)}
    {
    }
    
    my_custom_error& operator=(const my_custom_error& other)
    {
    	message = other.message;
    }
    
    const char* what() noexcept {
    	return message.c_str();
    }
};
try {
	throw my_custom_error("woah!");
} catch (const std::exception& e) {
	std::cerr << e.what() << '\n';
}

more than inheritance

std::variant<T1, T2, ..., TN>
  • holds object of any of types
T1, T2, ..., TN
  • index()
  • std::get<Index>(variant)
std::any
  • holds object of any type
  • type()
  • std::any_cast<Target>(any)

rtti

rtti

  • <typeinfo>, typeid, std::type_info, std::bad_typeid
  • std::type_info::hash_code, std::type_index
  • std::type_info::name(), symbol (de)mangling
  • dynamic_cast, downcast, upcast, sidecast, std::bad_cast
  • std::variant, std::monostate, std::bad_variant_access
  • std::any, std::any_cast
  • union

RAII

inheritance

virtual

  • constructor

  • member initializer list

  • destructor

  • default constructor

  • std::initializer_list

  • in-class initializer

  • copy ctor

  • cpy assign op

  • move ctor

  • mv assign op

  • default members

  • deleted members

  • rule of 0

  • rule of 3/5

  • smart-ptrs

  • converting constructor

  • explicit specifier

  • is-a relation

  • has-a relation

  • public inheritance

  • protected members

  • using declaration

  • final specifier

  • private & protected inheritance

  • multiple inheritance

  • diamond problem

  • empty base optimization

  • base method hiding problem (shadowing)

  • virtual methods

  • override specifier

  • dynamic type

    • RTTI

    • dynamic_cast

    • typeid

    • vtables

  • by value passing problem (slicing)

  • pure virtual function

  • abstract class

misc

  • friends

  • nested members

  • conversion operators

  • operator overloading

  • member pointers

  • unions

    • std optional

    • std variant

    • std any

type ereasure

pimpl

enable shared from this with private inheritance

linkage and static (non-member) functions

Liskov substitution principle

function member ref qualification

constexpr virtual

virtual inheritance

aggregate, pod, literal type

implement class in cpp file

delegation to sister class

RTTI

By Jan Bielak