c++无原生反射,rtti仅支持类型识别与安全转换,无法获取成员、方法等结构信息;需手动注册+宏辅助实现反射,第三方库如boost.pfr、rttr、refl-cpp各有局限。

C++ 没有原生反射机制,RTTI(typeid、dynamic_cast)仅支持有限的运行时类型查询,不能枚举成员、调用方法或获取字段名。
为什么 typeid 和 dynamic_cast 不算真正反射
它们只解决“这个对象到底是什么类型”和“能不能安全转成某类指针”这两个问题,不提供结构信息:
-
typeid(obj).name()返回的是编译器生成的、未修饰的内部名称(如"St6vectorIiSaIiEE"),不可读、不可靠、跨平台不一致 -
typeid无法获取基类列表、成员变量名、函数签名,也不能遍历类定义 -
dynamic_cast仅对带虚函数的多态类型有效;对struct、POD 类型、模板实例完全失效 - 所有 RTTI 信息在编译期固化,无法在运行时注册新类型或修改已有类型描述
想实现类似反射,主流做法是手动注册 + 宏辅助
这是目前最可控、兼容性最好、不依赖编译器扩展的方案(比如 Qt 的 Q_OBJECT、Boost.Describe 或自家宏系统):
- 用宏在类定义处声明反射元数据:
REFLECTABLE(int x, std::string name),宏展开为静态成员函数或友元特化 - 每个字段需显式命名,因为 C++ 标准不提供字段名字符串化能力(
__FIELD_NAME(x)这类宏本质是字符串字面量拼接) - 注册表通常用
std::map<:string fieldinfo></:string>存储,键为字段名,值含偏移量、类型 ID、getter/setter 函数指针 - 注意:涉及虚继承、多重继承时,
offsetof可能失效,必须用reinterpret_cast+ 成员指针运算替代
第三方库选型关键差异点
不同库解决的问题粒度不同,别默认“用了就等于有反射”:
立即学习“C++免费学习笔记(深入)”;
-
Boost.PFR:仅支持无用户定义构造/析构/虚函数的平凡类型(std::is_trivially_copyable_v),可自动推导字段数和偏移,但不支持函数、访问控制、模板参数提取 -
RTTR:功能全,支持方法调用、属性读写、枚举注册,但需在编译期注册(RTTR_REGISTRATION),且运行时性能开销明显(哈希查找 + 类型擦除) -
refl-cpp:C++20 编译期反射实验库,依赖consteval和结构化绑定,不支持 MSVC 2022 17.4 前版本,且无法反射私有成员(除非加friend) - 所有库都无法反射模板实参名(
std::vector<int></int>中的int可获取,但无法知道它原本叫T)
最容易被忽略的坑:调试信息 ≠ 反射可用数据
有人试图从 DWARF 或 PDB 解析类型信息,这条路走不通:
- 调试符号不是 ABI 一部分,发布版通常 strip 掉,
libdwarf/DbgHelp在生产环境不可靠 - 即使解析成功,也无法安全地把字段名映射到内存偏移(优化可能重排、内联、寄存器提升)
- 没有标准方式将调试信息中的类型 ID 转为
std::type_info*或用于dynamic_cast - 这种方案会让程序强依赖构建环境和调试格式,彻底失去可移植性
真要动态类型交互,与其折腾调试信息,不如用 JSON Schema + 手动映射,或者换语言。










