C++原生不支持反射是因设计取舍而非技术限制:为保持零成本抽象和避免元数据膨胀,标准未扩展RTTI至字段/方法级;所谓“反射”实为宏、模板或代码生成等人工补全手段。

为什么 C++ 原生不支持反射
C++ 标准至今(C++20/C++23)没把运行时类型信息(RTTI)扩展到字段、方法、构造函数的自动枚举上,typeid 和 dynamic_cast 只能告诉你“是不是某个类”,不能列出它的 name 成员或调用 set_value()。这不是编译器偷懒,而是设计取舍:避免元数据膨胀、保持零成本抽象。所以所谓“C++ 反射”,本质是人肉补全——要么在编译期生成代码,要么靠宏/模板硬编码结构描述。
用宏 + 类型擦除模拟构造函数调用
最常用且可控的做法:为每个需“反射实例化”的类,显式注册其构造逻辑。不是靠解析符号表,而是靠你写一行宏,它就生成一个 std::function<:unique_ptr>()></:unique_ptr> 的工厂函数。
常见错误现象:undefined reference to 'create_Foo()' —— 忘了在 .cpp 里展开宏定义;或者用了 inline 但头文件没被所有用到的地方包含。
- 使用场景:插件系统、配置驱动的对象创建(如 JSON 字段名映射到类名)
- 参数差异:构造参数必须固定(比如全用默认值),或统一走
std::any/json参数包,后者需额外做类型转换桥接 - 性能影响:一次
std::map<:string factory_fn>::at()</:string>查表 + 函数调用,比直接new Foo多 100ns 左右,通常可接受 - 示例注册:
#define REGISTER_CLASS(cls) \ static std::unique_ptr<Base> create_##cls() { return std::make_unique<cls>(); } \ static const bool registered_##cls = []{ factories()[""#cls] = &create_##cls; return true; }();
用 constexpr + 模板推导字段名(C++20 起可行)
如果只要编译期知道字段名和偏移(不涉及运行时字符串查找),std::tuple 配合 REFLECT 宏能实现轻量“伪反射”。但它不等于 Java 的 Field.get(),无法通过字符串动态读写任意字段。
立即学习“C++免费学习笔记(深入)”;
容易踩的坑:std::string_view 字面量在 constexpr 上下文里合法,但若尝试拼接(如 "field_" + std::to_string(i))会直接编译失败;字段顺序必须和 std::tuple 一致,否则 get<i>(t) 拿错值。
- 适用场景:序列化/打印调试信息、生成结构体 Schema 文本
- 兼容性注意:GCC 11+ / Clang 13+ 对
constexpr字符串操作支持较好,MSVC 2022 17.4 后才稳定 - 关键限制:无法处理继承层次中的虚函数表、private 字段不可见、数组成员需手动展开
- 简例:
struct Person { int age; const char* name; };<br>REFLECT(Person, age, name); // 展开为 constexpr 字段名数组和访问器
别碰运行时符号解析(dlsym / GetModuleHandle)
有人想用动态库符号表绕过编译期限制,比如根据字符串 "MyClass" 找到 _Z10MyClassC1v 并调用。这在 Linux 下看似可行,但实际极脆弱:
- 名字修饰(mangling)依赖编译器、标准库、ABI 版本,换个 GCC 小版本就可能找不到符号
- Release 模式下 LTO 或
-fvisibility=hidden会让符号彻底消失 - Windows 上
GetProcAddress对 C++ 成员函数根本无效(只能导出 C 风格函数) - 即使成功,你也得不到对象布局信息,无法安全构造或析构
真要跨语言或热重载,老实用 IDL(如 Protobuf)或 DSL 描述结构,再生成 C++ 绑定代码——那才是可控的“反射”起点。
复杂点在于:没有银弹。选宏注册,就得维护注册表一致性;选 constexpr 反射,就得接受它只服务编译期;想完全自动化?得引入外部代码生成工具,而那已经不属于“C++ 本身”了。










