std::type_index能作map键因其重载了operator

std::type_index 可以直接当 std::map 或 std::unordered_map 的键用,但必须配合 typeid 构造,不能直接传类型名或模板参数。
为什么 std::type_index 能作 map 键
因为 std::type_index 重载了 operator 和 <code>operator==,还提供 hash_code(),所以它天然支持:std::map<:type_index t></:type_index>(有序)和 std::unordered_map<:type_index t></:type_index>(无序)。而裸的 std::type_info& 不可拷贝、不可存储、无法比较,根本进不了容器。
常见错误现象:
- 直接写
std::map<decltype int></decltype>—— 编译失败,std::type_info禁止拷贝 - 试图用
typeid(int)当键插入std::map—— 报错 “use of deleted function” - 用
std::type_index{typeid(T)}但忘了加花括号,写成std::type_index(typeid(T))—— 某些编译器会触发最令人头疼的“最令人烦恼的解析”(most vexing parse)
std::map<:type_index t> 的典型初始化方式
工厂注册、插件系统、序列化类型路由等场景,都需要运行时按类型查值。关键点是:所有键必须来自 typeid 表达式,且推荐显式构造 std::type_index。
示例(安全写法):
std::map<:type_index std::function>()>> factory;
factory[std::type_index{typeid(ConcreteA)}] = [] { return std::make_unique<concretea>(); };
factory[std::type_index{typeid(ConcreteB)}] = [] { return std::make_unique<concreteb>(); };
// 使用时:
auto it = factory.find(std::type_index{typeid(ConcreteA)});
if (it != factory.end()) {
auto obj = it->second();
}
</concreteb></concretea></:type_index>
注意:
- 不要用
typeid(T).name()字符串做键——名字不跨编译器稳定,且无类型语义 - 模板函数内获取
std::type_index必须作用于具体对象或带括号的表达式,如std::type_index{typeid(T{})},避免依赖未定义行为 -
std::type_index对同一类型总是生成相同 hash 和比较结果,哪怕在不同编译单元中
std::unordered_map<:type_index t> 的哈希注意事项
虽然 std::type_index 提供 hash_code(),但标准库没为它特化 std::hash,所以直接写 std::unordered_map<:type_index t></:type_index> 会编译失败(缺少 std::hash<:type_index></:type_index>)。
解决方法只有两种:
- 手动提供哈希仿函数:
struct type_index_hash { size_t operator()(const std::type_index& ti) const { return ti.hash_code(); } };,然后声明为std::unordered_map<:type_index t type_index_hash></:type_index> - 或用
boost::hash<:type_index></:type_index>(如果项目已引入 Boost)
别尝试自己对 ti.name() 做哈希——字符串内容不可靠,且 name() 返回的是 const char*,不是 std::string,容易悬垂。
跨 DLL/so 时 std::type_index 是否仍可靠
在 Windows MSVC 下,若类型定义在 DLL 中,且调用方与 DLL 使用不同 CRT 实例,typeid 可能返回不同 std::type_info 地址,导致 std::type_index 比较失败。这是少数真正踩坑的地方。
应对策略:
- 确保所有模块使用 /MD(动态 CRT),而非 /MT
- 类型定义尽量放在主程序或统一导出的头文件中,避免在多个 DLL 中重复定义同一类
- 必要时改用字符串 ID + 显式注册表(牺牲类型安全换稳定性)
Linux GCC/Clang 下这个问题较轻,但仍有 ABI 版本兼容风险——比如用 GCC 12 编译的 type_index,不能安全地和 GCC 13 生成的比对。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











