可行但需规避三类坑:生命周期(禁用&typeid(T),须用std::type_index{typeid(T)})、类型修饰符(注册与查询类型必须完全一致,推荐退修饰)、哈希器(RTTI未启用或旧编译器可能导致unordered_map失效,建议优先用map)。

直接用 std::type_index 作异构容器(如 std::map 或 std::unordered_map)的键是可行的,但只要踩中生命周期、类型修饰符、哈希器这三类坑中的任意一个,find() 就会返回 end(),甚至触发未定义行为——不是代码写错了,而是语义理解偏差了。
std::type_index{typeid(T)} 必须用花括号初始化,禁用 &typeid(T)
错误写法:factory[&typeid(MyClass)] = handler; —— typeid(MyClass) 是临时 const std::type_info&,取地址后指针立即悬垂,后续比较或哈希就是未定义行为。
正确做法始终是让 std::type_index 自行管理内部指针生命周期:
-
std::type_index{typeid(MyClass)}:适用于已知具体类型的注册场景 -
std::type_index{typeid(*ptr)}:多态对象必须解引用,typeid(ptr)返回的是指针类型(如Base*),不是实际派生类 -
std::type_index{typeid(std::remove_cv_t<:remove_reference_t>>{})}</:remove_reference_t>:对变量x退修饰后构造,避免const MyClass&和MyClass键不匹配
注册与查询的类型修饰符必须完全一致
std::type_index 对 const、volatile、引用(&、&&)、指针(*)完全敏感。它反映的是 typeid 表达式的完整声明类型,不是“逻辑上同一个类型”。
典型翻车点:
- 工厂注册用
typeid(ConcreteA),调用方传入const ConcreteA& x后却查typeid(x)→find()永远返回end() - 模板函数里写
typeid(T),当T = const int时,和外部注册的typeid(int)对不上 - 对多态指针
Base* ptr,误用typeid(ptr)→ 得到的是Base*类型键,应改用typeid(*ptr)
统一建议:注册和查询都基于“值类型”,即 std::type_index{typeid(T)}(T 是非引用、非 const 的完整类型),或显式退修饰:std::type_index{typeid(std::remove_cv_t<t>{})}</t>。
std::unordered_map<:type_index t> 编译失败?优先验证哈希器和 RTTI
虽然 C++11 标准要求特化 std::hash<:type_index></:type_index>,但 GCC 4.8、MSVC 2013 等旧环境,或项目禁用 RTTI(-fno-rtti)时,仍可能报 “no matching function for call to ‘hash’”。
验证方式:
-
static_assert(std::is_default_constructible_v<:hash>>);</:hash>—— 若断言失败,说明哈希器不可用 - 确认编译标准至少为
-std=c++11,且未加-fno-rtti - 确认头文件完整:
#include <typeindex></typeindex>和#include <typeinfo></typeinfo>都不可少
实操建议:
- 最简方案:改用
std::map<:type_index t></:type_index>——std::type_index已重载operator,无需额外配置,兼容性零风险 - 若坚持用
std::unordered_map,显式传哈希器:std::unordered_map<:type_index t std::hash>></:type_index> - 别自己封装哈希器去调
ti.hash_code()—— 它本就是std::hash特化的底层依据,重复实现无意义还易出错
std::type_index{typeid(T)} 在模板中非法,需改用 typeid(T{}) 或 std::declval
typeid(T) 在模板中是非法的:T 是未实例化的类型名,不是表达式。某些编译器可能静默接受,但行为未定义;更常见的是编译失败或生成错误的键值。
正确写法:
-
std::type_index{typeid(T{})}:要求T可默认构造,简洁安全 -
std::type_index{typeid(std::declval<t>())}</t>:更通用,适用于不可默认构造的类型,需#include <utility></utility> - 最稳妥写法(非模板上下文):
std::type_index{typeid(ConcreteA)}—— 明确、无歧义、不依赖模板实例化位置
特别注意:std::type_index 不保证跨进程、跨次运行的 hash_code() 值一致,不能用于持久化或网络传输;它只在单次运行内可靠,且依赖 RTTI 开启和完整类型定义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











