std::type_index用作异构容器键需谨慎:须启用RTTI、包含和、用花括号初始化、注意类型修饰符一致性,且哈希值不可跨进程持久化,应优先用std::map而非unordered_map。

std::type_index 可以直接用作异构容器的键,但不能无脑塞进 std::unordered_map,也不能靠 typeid(x) 直接构造——否则大概率查不到、崩溃、或跨编译单元失效。
为什么 std::unordered_map<:type_index t></:type_index> 编译失败
标准虽要求 C++11 起提供 std::hash<:type_index></:type_index> 特化,但 GCC 4.8、MSVC 2013 及更早版本、禁用 RTTI(-fno-rtti)、或某些定制 STL 实现中它并不存在。报错典型为:no matching function for call to 'hash'。
- 验证是否可用:
static_assert(std::is_default_constructible_v<:hash>>);</:hash> - 若断言失败,显式传入哈希器:
std::unordered_map<:type_index t std::hash>></:type_index> - 更稳妥做法:改用
std::map<:type_index t></:type_index>—— 它只依赖operator,而 <code>std::type_index已完整支持,零兼容性风险
std::type_index{typeid(T)} 必须用花括号初始化
写 factory[&typeid(MyClass)] = handler; 是危险操作:typeid(MyClass) 是临时对象,取地址后立即悬垂;写 std::type_index(typeid(MyClass)) 还可能触发 most vexing parse(被解析为函数声明)。
- 正确写法只有:
std::type_index{typeid(MyClass)}、std::type_index{typeid(*ptr)}、std::type_index{typeid(std::remove_cvref_t<decltype>{})}</decltype> - 对模板参数
T,避免typeid(T)(非法),改用typeid(std::remove_cvref_t<t>{})</t>或std::declval<t>()</t> - 多态指针必须解引用:
typeid(ptr)得到的是Base*类型,typeid(*ptr)才是实际派生类
注册和查询的类型修饰符必须完全一致
std::type_index 对 const、&、* 敏感,不是“语义等价”就相等——它是 typeid 的忠实镜像。
立即学习“C++免费学习笔记(深入)”;
- 注册用了
std::type_index{typeid(MyClass)},查询却传std::type_index{typeid(const MyClass&)}→find()永远返回end() - 对变量
const T& x,别用typeid(x),应统一退修饰:std::type_index{typeid(std::remove_cvref_t<decltype>{})}</decltype> - 与
std::any配合时,必须用std::type_index{a.type()},不可缓存a.type()返回的引用并后续构造 —— 生命周期绑定在std::any实例上
RTTI 必须启用且头文件不能少
std::type_index 依赖 typeid 的运行时行为,而 typeid 在无虚函数的类上返回静态类型,在多态类上才返回动态类型——这本身不是 bug,但若 RTTI 关闭,typeid 行为未定义,整个链路就崩了。
- 编译器选项检查:
-frtti(GCC/Clang,默认开启),MSVC 确认/GR已设 - 头文件缺一不可:
#include <typeindex></typeindex>和#include <typeinfo></typeinfo> - 加个兜底断言:
static_assert(__GXX_RTTI || _MSC_VER, "RTTI must be enabled");
真正容易被忽略的点是:std::type_index::hash_code() 值不跨进程稳定,不能存日志、不能发网络、不能当持久 ID 用;唯一可靠的判断方式永远是 == 比较,不是哈希值相等。


















