typeid 返回的 name() 字符串不可直接用作类名,因其为编译器特定的 mangled 名称(如 gcc 输出 "n5mylib7myclasse"),不可读、不可移植,不适用于日志、反射或字符串比较。

typeid 返回的 name() 字符串不可直接用作类名
在 C++ 中,typeid 是运行时获取类型信息的唯一标准方式,但它返回的 name() 结果是编译器实现定义的:GCC 输出类似 "N5mylib7MyClassE" 的 mangled 名字,MSVC 也类似,Clang 略有不同。它**不是可读类名**,也不能直接用于日志、反射或字符串比较。
常见错误是这样写:
std::cout << typeid(MyClass).name() << std::endl; // 输出乱码,非 "MyClass"
- 必须调用
abi::__cxa_demangle(GCC/Clang)或UnDecorateSymbolName(MSVC)才能还原可读名 -
typeid对带 cv 限定符的类型(如const T&)会返回修饰后的类型,和原始类名不一致 - 若对象为多态类型且指针/引用指向派生类,
typeid(*ptr)返回实际动态类型;但对非虚类,typeid只看静态类型
获取可读类名的跨平台最小可行方案
没有标准库函数能直接返回干净类名,但可以封装一个轻量 demangle 工具。GCC/Clang 下依赖 <cstdlib></cstdlib> 和 <memory></memory>,MSVC 下用 Dbghelp.h(需链接 dbghelp.lib)。
最简实用写法(仅 GCC/Clang):
立即学习“C++免费学习笔记(深入)”;
#include <typeinfo>
#include <memory>
#include <cstdlib>
std::string type_name(const std::type_info& ti) {
int status = 0;
std::unique_ptr<char, void(*)(void*)> res{
abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status),
std::free
};
return (status == 0) ? res.get() : ti.name();
}
// 用法
std::cout << type_name(typeid(MyClass)) << std::endl; // 输出 "MyClass"
- 注意:
abi::__cxa_demangle不是标准 API,但 GCC/Clang 公开提供,稳定可用 - 返回值为
std::string,避免裸指针生命周期问题 - MSVC 用户可改用
UnDecorateSymbolName,但需处理缓冲区长度和前缀(如去掉"class "或"struct ")
typeid 在模板和泛型代码中的典型误用
模板中常想“在编译期知道类型名”,但 typeid 是纯运行时机制,无法参与 constexpr 或 SFINAE 判断。例如:
template<typename T>
void log_type() {
std::cout << typeid(T).name(); // 运行时才求值,无法用于 static_assert 或 if constexpr
}
- 若需编译期类名,只能靠宏(如
#define CLASS_NAME(x) #x)或 C++20std::source_location::function_name()(间接推断) -
typeid(T) == typeid(U)比较的是类型等价性,但对模板参数可能因 cv/ref 修饰失败,应优先用std::is_same_v<t u></t> - 对空基类或无虚函数的类,
typeid开销极小;但对多态类,首次调用可能触发 vtable 查找(实际影响微乎其微)
调试时快速打印类名的偷懒技巧
开发阶段不想写 demangle 封装?可以用预处理器辅助:
#define TYPE_NAME(T) #T
// 用法
struct Foo {};
std::cout << TYPE_NAME(Foo) << std::endl; // 输出 "Foo"
但这只适用于**字面类型名已知**的场景,无法用于变量推导:
Foo f; std::cout << TYPE_NAME(decltype(f)); // 输出 "decltype(f)",不是 "Foo"
- 配合
auto+ IDE 快捷键(如 VS 的 Ctrl+Shift+Space)看推导类型,比运行时typeid更快 - GDB/Lldb 调试时直接用
ptype var或whatis var,比代码里硬塞typeid更可靠 - 真正需要运行时类名(如插件系统、序列化),建议用宏注册 + 映射表,而非依赖
typeid().name()
typeid 的核心价值不在“取名字”,而在类型安全的运行时分发(比如配合 std::any 或自定义 variant)。把精力花在 demangle 上,不如先确认你是否真的需要运行时类名——多数时候,编译期宏或调试器更准、更快、更稳。











