直接用 std::type_index 做异构容器键可行但易踩坑:需花括号初始化、退修饰、保持类型修饰符一致、启用 RTTI,且 hash_code() 不可跨进程使用,可靠比较只用 ==。

直接用 std::type_index 做异构容器(比如 std::map 或 std::unordered_map)的键是可行的,但只要踩中生命周期、类型修饰符、哈希器这三类坑中的任意一个,就会出现查不到、崩溃、跨编译单元行为不一致——而且往往静默失败,很难 debug。
std::type_index{typeid(T)} 必须用花括号初始化,禁用 &typeid(T)
错误写法:factory[&typeid(MyClass)] 或 std::type_index(typeid(MyClass))(可能被解析为函数声明)会触发未定义行为。
原因: typeid 表达式返回临时 const std::type_info&,取地址后得到悬垂指针;std::type_index 内部只存一个 const std::type_info*,必须由它自己管理生命周期。
- ✅ 正确写法统一用花括号:
std::type_index{typeid(MyClass)}、std::type_index{typeid(*ptr)} - ✅ 模板内要退修饰:
std::type_index{typeid(std::remove_cv_t<:remove_reference_t>>{})}</:remove_reference_t> - ❌ 禁止在模板参数未实例化时写
typeid(T)—— 这是非法表达式 - ❌ 不要用
decltype(typeid(T)),返回的是不可拷贝的引用类型
注册和查询的类型修饰符必须完全一致
std::type_index 对 const、volatile、引用、指针完全敏感。哪怕只是声明差异,find() 就返回 end()。
立即学习“C++免费学习笔记(深入)”;
- 注册用了
std::type_index{typeid(MyClass)},查询却传std::type_index{typeid(const MyClass&)}→ 查不到 - 对多态指针
Base* ptr,必须用std::type_index{typeid(*ptr)}获取实际派生类型;typeid(ptr)返回的是Base*类型,不是对象类型 - 统一约定:工厂注册和运行时查询都基于“值类型”,即
std::type_index{typeid(ConcreteA)},而不是typeid(ConcreteA&)或typeid(const ConcreteA) - 接收
const T&参数时,别用typeid(obj),改用std::type_index{typeid(T)}(取模板参数裸类型)
std::unordered_map<:type_index t> 编译失败?先验证哈希器
虽然 C++11 标准要求特化 std::hash<:type_index></:type_index>,但 GCC 4.8、MSVC 2013 及更早版本、或禁用 RTTI 的构建环境里,std::unordered_map<:type_index t></:type_index> 仍会报 “no matching function for call to ‘hash’”。
- ✅ 验证方式:
static_assert(std::is_default_constructible_v<:hash>>);</:hash>—— 不通过就得干预 - ✅ 优先用
std::map<:type_index t></:type_index>:无需哈希器,operator<已就绪,性能差异在绝大多数场景下可忽略 - ✅ 若坚持用
unordered_map,显式传入标准哈希器:std::unordered_map<:type_index t std::hash>></:type_index> - ❌ 别自己写哈希仿函数调
ti.hash_code()—— 它本就是std::hash特化的底层依据,重复封装无意义且易错 - ⚠️ 必须开启 RTTI(
-frtti//GR),否则typeid行为未定义,std::type_index构造看似成功,实则比较永远为 false
别把 hash_code() 当类型 ID 用
std::type_index::hash_code() 的值只保证单次进程内哈希一致性,**不能用于持久化、日志记录、网络传输或跨进程比对**。
- 同一类型在不同进程、甚至同程序重启后,
hash_code()值都可能变化 - 真正可靠的判断永远是
==比较,不是哈希值相等 - 不要存
hash_code()到数据库、发给其他服务、或基于它做路由决策 - 若需稳定标识,应结合
name()+ 白名单校验(但依然不推荐)
最易忽略的一点是 RTTI:关掉之后不会编译报错,typeid 和 std::type_index 会静默失效,所有查找都失败,而你可能还在怀疑是不是类型写错了。


















