C++标准不支持运行时反射,但可通过宏、模板和类型擦除模拟:1.用宏注册类名与字段偏移;2.用std::any读取字段值;3.用std::function注册并调用成员函数;4.需注意standard-layout限制及类型安全问题。

纯C++标准不支持运行时反射,但可以通过宏、模板和类型擦除等手段模拟出简易的反射能力,比如获取类名、枚举字段名、调用成员函数等。下面是一个轻量、可扩展、不依赖第三方库的实现思路。
1. 基于宏的类名注册与字段映射
利用宏在定义类时自动注册元信息,避免手写重复代码。核心是为每个类生成一个静态方法,返回字段名与偏移量/访问器的映射表。
示例:实现 类名获取 和 简单字段遍历
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <typeinfo>
<p>// 反射基础结构
struct FieldInfo {
std::string name;
size_t offset;
const std::type_info& type;
};</p><p>struct ClassInfo {
std::string name;
std::vector<FieldInfo> fields;
static const std::map<std::string, const ClassInfo*>& GetAll();
};</p><p>// 宏:声明反射支持(用于类外)</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/6e7abc4abb9f" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">C++免费学习笔记(深入)</a>”;</p><h1>define REFLECTABLE_CLASS(name) \</h1><pre class="brush:php;toolbar:false;">static const ClassInfo& GetClassInfo(); \
virtual const ClassInfo& GetClass() const { return GetClassInfo(); }// 宏:定义反射信息(在类定义后使用)
define BEGIN_REFLECT(name) \
const ClassInfo& name::GetClassInfo() { \
static ClassInfo info{#name, std::vector<FieldInfo>{} }; \
static bool inited = false; \
if (!inited) {define FIELD(member) \
info.fields.push_back({#member, offsetof(name, member), typeid(decltype(name::member))});define END_REFLECT() \
inited = true; \
} \
return info; \
}// 使用示例 struct Person { std::string name; int age; double score;
REFLECTABLE_CLASS(Person)
};
BEGIN_REFLECT(Person) FIELD(name) FIELD(age) FIELD(score) END_REFLECT()
2. 运行时打印对象字段值(需类型安全转换)
借助 reinterpret_cast 和 std::any(C++17)或自定义类型擦除,可读取字段值。以下用 std::any 简化演示:
#include <any>
<p>std::map<std::string, std::any> ReflectGetValues(const void<em> obj, const ClassInfo& info) {
std::map<std::string, std::any> result;
for (const auto& f : info.fields) {
if (f.name == "name") {
result[f.name] = std::any_cast<std::string>(
</em>static_cast<const std::string<em>>(
static_cast<const char</em>>(obj) + f.offset));
} else if (f.name == "age") {
result[f.name] = <em>static_cast<const int</em>>(static_cast<const char<em>>(obj) + f.offset);
} else if (f.name == "score") {
result[f.name] = </em>static_cast<const double<em>>(static_cast<const char</em>>(obj) + f.offset);
}
}
return result;
}</p><p>// 使用
int main() {
Person p{"Alice", 30, 95.5};
auto vals = ReflectGetValues(&p, p.GetClass());
for (const auto& [k, v] : vals) {
std::cout << k << ": ";
if (v.type() == typeid(std::string))
std::cout << std::any_cast<std::string>(v);
else if (v.type() == typeid(int))
std::cout << std::any_cast<int>(v);
else if (v.type() == typeid(double))
std::cout << std::any_cast<double>(v);
std::cout << "\n";
}
}
</p>3. 支持简单方法反射(通过函数对象注册)
为类添加可调用方法的元信息,例如注册无参无返回值的成员函数:
using MethodFunc = std::function<void(void*)>;
<p>struct MethodInfo {
std::string name;
MethodFunc func;
};</p><p>struct ClassInfo {
// ... 字段同上
std::vector<MethodInfo> methods;
};</p><h1>define METHOD(func) \</h1><pre class="brush:php;toolbar:false;">info.methods.push_back({#func, [](void* obj) { \
static_cast<name*>(obj)->func(); \
}});// 在 Person 中加一个方法: void SayHello() { std::cout
// 然后在 END_REFLECT 前加 METHOD(SayHello)
// 调用方式: void ReflectCallMethod(void* obj, const ClassInfo& info, const std::string& method_name) { for (const auto& m : info.methods) { if (m.name == method_name) { m.func(obj); return; } } }
4. 注意事项与局限性
- offsetof 要求类型是标准布局(standard-layout),不能有虚函数、多继承、非公有非静态数据成员等
- 字段访问未做类型安全校验,实际项目建议配合
if constexpr+ 模板特化增强健壮性 - 不支持嵌套对象、数组、指针字段的自动展开,需手动处理
- 宏方案对 IDE 友好性较差(跳转/补全可能失效),适合小型工具或配置驱动场景
- 如需完整反射,推荐成熟方案:Boost.Hana(编译期)、RTTR(运行时)、or C++23 的反射 TS(尚未稳定)









