C++原生不支持属性反射,因编译后成员名丢失,需用宏+offsetof+typeid手动构建元数据系统;示例方案通过REFLECT_FIELD和BEGIN/END_REFLECT宏生成字段信息,配合偏移量实现读写,轻量但不支持继承与cv限定符。

为什么C++原生不支持属性反射
C++标准里根本没有“属性反射”这个概念,class成员变量在编译后就只剩偏移量和类型信息,运行时连名字都丢了。你写obj.name,编译器直接换成*(char*)(&obj + 8)这种指针运算,根本不会保留"name"字符串。所以所谓“实现反射”,本质是手动补全这些丢失的信息——不是让语言变魔法,而是用宏+模板+注册表自己搭一套元数据系统。
用宏+结构体注册实现最简属性映射
最轻量、零依赖的做法:为每个需要反射的类定义一个静态函数,返回std::vector描述其字段(名字、偏移、类型ID)。宏用来避免手写重复代码:
#define REFLECT_FIELD(cls, name, type) \
{ #name, offsetof(cls, name), typeid(type).hash_code() }
<h1>define BEGIN_REFLECT(cls) \</h1><p>static const std::vector<FieldInfo> GetFields() { \
return {</p><h1>define END_REFLECT() \</h1><pre class='brush:php;toolbar:false;'>}; \}
用法示例:
立即学习“C++免费学习笔记(深入)”;
struct Person {
std::string name;
int age;
BEGIN_REFLECT(Person)
REFLECT_FIELD(Person, name, std::string),
REFLECT_FIELD(Person, age, int)
END_REFLECT()
};
关键点:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
offsetof必须用标准库<cstddef>,不能对引用/非POD类型用 -
typeid(T).hash_code()只是粗略区分类型,int和long在某些平台可能冲突,真要精确得用std::type_info::name()或自定义类型ID - 宏展开后每个
REFLECT_FIELD生成一个临时FieldInfo对象,构造开销小但不可用于constexpr上下文
通过void*和偏移量读写字段值
有了字段描述,就能用reinterpret_cast做通用读写。注意:必须确保对象生命周期有效,且字段访问符合严格别名规则:
template<typename T>
T GetFieldValue(const void* obj_ptr, size_t offset) {
return *reinterpret_cast<const T*>(static_cast<const char*>(obj_ptr) + offset);
}
<p>template<typename T>
void SetFieldValue(void<em> obj_ptr, size_t offset, const T& value) {
</em>reinterpret_cast<T<em>>(static_cast<char</em>>(obj_ptr) + offset) = value;
}</p>调用示例:
Person p{"Alice", 30};
auto name = GetFieldValue<std::string>(&p, Person::GetFields()[0].offset);
SetFieldValue<int>(&p, Person::GetFields()[1].offset, 31);
风险提示:
- 如果字段是
std::string这类非平凡类型,GetFieldValue只是浅拷贝指针,实际调用的是std::string的拷贝构造——这没问题;但若字段是std::vector<int>,同样成立 - 绝对不要对
union或有虚函数的类用offsetof,它可能返回错误偏移 -
void*传参容易误传地址而非对象本身,建议封装成template<typename T> T& GetRef(...)带类型检查
为什么不用Boost.Hana或RTTR这类库
它们确实更强大,但引入后编译时间暴涨、模板深度爆炸、错误信息难以阅读。比如RTTR的RTTR_REGISTER_TYPE宏会生成大量隐藏模板实例化,一个10字段的类可能触发200+个模板特化。而手写宏方案编译快、调试直观、可控性强——尤其当你只需要序列化到JSON或做简单编辑器绑定时,多出来的功能全是负担。
真正麻烦的是继承链处理和const/volatile限定符支持,这些一旦加进去,宏就会迅速失控。多数项目卡在这一步就退回手写ToJson()函数了。

















