std::type_index用作异构容器键需确保构造方式、类型修饰、哈希器三者对齐:必须作用于可求值表达式(如typeid(t{})或typeid(std::declval())),统一使用裸类型避免const/&/∗差异,启用rtti并包含和。

std::type_index 可以直接用作异构容器的键,但查不到、崩溃、跨编译单元不一致——八成不是逻辑问题,而是构造方式、类型修饰、哈希器三处没对齐。
std::type_index{typeid(T)} 在模板中非法?用 std::declval 或 T{} 替代
模板参数 T 不是表达式,typeid(T) 是语法错误(某些编译器会静默接受但行为未定义)。必须让 typeid 作用于一个“可求值的表达式”。
- 若
T可默认构造:用std::type_index{typeid(T{})} - 更通用写法(推荐):用
std::type_index{typeid(std::declval<t>())}</t>,需#include <utility></utility> - 非模板上下文最稳妥:直接写死类型,如
std::type_index{typeid(ConcreteA)} - 绝对不要写
std::type_index(typeid(T))——可能被解析为函数声明(most vexing parse)
注册和查询的类型修饰不一致 → find() 永远返回 end()
std::type_index 对 const、&、* 零容忍:哪怕只差一个 const,就是两个完全不同的键。这不是 bug,是设计使然。
- 注册工厂时统一用裸值类型:
factory[std::type_index{typeid(MyClass)}] = []{ return std::make_unique<myclass>(); };</myclass> - 对引用参数
const T& x,查类型应取std::type_index{typeid(T)},而非typeid(x)(后者得const T&) - 对多态指针
Base* ptr,必须解引用:std::type_index{typeid(*ptr)};typeid(ptr)返回的是Base*类型 - 退修饰推荐组合:
std::type_index{typeid(std::remove_cvref_t<decltype>{})}</decltype>
std::unordered_map<:type_index t> 编译失败?先验证哈希器是否就绪
标准从 C++11 起要求特化 std::hash<:type_index></:type_index>,但 GCC 4.8、MSVC 2013 等旧环境仍可能缺失,报错类似 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已完整支持 - 禁止自行封装
ti.hash_code()做哈希器——std::hash特化已基于它实现,重复封装反而破坏一致性
RTTI 未启用或头文件遗漏 → 行为未定义
std::type_index 依赖运行时类型信息(RTTI)。禁用 RTTI 或漏头文件会导致 typeid 表达式行为未定义,进而使所有查找失效。
- 检查编译器选项:GCC/Clang 确保未加
-fno-rtti;MSVC 确认开启/GR - 必须包含两个头文件:
#include <typeindex></typeindex>和#include <typeinfo></typeinfo> - 加一行防御性断言:
static_assert(__GXX_RTTI || _MSC_VER, "RTTI must be enabled"); - 注意:
typeid(T).name()绝对不能当键用——字符串由编译器生成,跨平台不可比
最容易被忽略的是:类型修饰统一这件事,必须贯穿注册、查询、转发、模板推导全过程;哪怕只有一处用了 typeid(const T&),而其余地方用 typeid(T),整个检索链就断了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











