C++ 标准本身不支持运行时反射(如 Java 或 C# 那样通过 type_info 获取字段名、调用方法),但可以用宏 + 静态注册 + 类型擦除组合出一套轻量、可控的“伪反射”机制,适用于序列化、配置绑定、调试打印等场景。
typeid 或 std::type_info
std::type_info::name() 返回的是编译器生成的 mangled 名字(如 "St6vectorIiSaIiEE"),不可读、不可靠、无字段信息;typeid 也无法枚举类成员、获取字段偏移或类型元数据。真正需要的是开发者主动声明的结构化元数据。
关键思路:在类定义内部用宏展开出静态成员函数,返回该类型的字段描述表(std::vector),每个 FieldMeta 包含字段名、类型 ID、内存偏移、getter/setter 函数指针。
常见错误是宏里直接写 this->field —— 这会导致编译失败,因为宏展开时不在成员函数上下文中。正确做法是用 lambda 或函数指针捕获偏移量。
实操建议:
REFLECTABLE,接受字段列表,如 REFLECTABLE((int, age), (std::string, name))
offsetof 计算偏移,配合 static_cast 转成 void* 基址加法实现安全访问typeid,改用枚举(如 TYPE_INT, TYPE_STRING)或 std::type_index,避免 RTTI 依赖std::is_standard_layout_v 为 true),否则 offsetof 行为未定义没有编译器支持,就不存在“自动发现字段”。所有反射能力都依赖显式注册 —— 这不是缺陷,而是可控性的代价。
两种常见注册方式对比:
static const auto& reg = []{ ... }();,利用静态变量初始化顺序触发注册,但跨 TU 可能失效(ODR 问题)ReflectRegistry::Register(&Person::GetMeta); ,启动时集中调用,稳定可靠,适合大型项目consteval + 结构化绑定推导字段,但无法绕过“必须写宏声明”这一前提以下代码实现了字段名遍历和值读取,不含 setter 和嵌套对象,但已覆盖 80% 的配置/序列化需求:
#include#include #include #include #include struct FieldMeta { const char* name; std::type_index type; size_t offset; };
template
class Reflectable { public: static const std::vector
& GetFields() { static const std::vector fields = T::kFields; return fields; } }; define REFLECTABLE(...) \
static const std::vectorzuojiankuohaophpcnFieldMetayoujiankuohaophpcn kFields; \ static const std::vectorzuojiankuohaophpcnFieldMetayoujiankuohaophpcn BuildFields() { \ return { __VA_ARGS__ }; \ } \ static_assert(true, "")define FIELD(type, name) { #name, std::type_index(typeid(type)), offsetof(THIS_TYPE, name) }
// 使用示例 struct Person { int age; std::string name;
REFLECTABLE( { "age", std::type_index(typeid(int)), offsetof(Person, age) }, { "name", std::type_index(typeid(std::string)), offsetof(Person, name) } );};
// 手动注册(避免宏中无法推导 THIS_TYPE) const std::vector
Person::kFields = Person::BuildFields(); int main() { Person p{42, "Alice"}; for (const auto& f : Reflectable
::GetFields()) { std::cout reinterpret_cast >( reinterpret_cast >(&p) + f.offset ); } else if (f.type == std::type_index(typeid(std::string))) { std::cout reinterpret_cast >( reinterpret_cast >(&p) + f.offset ); } std::cout 注意
offsetof对非标准布局类型(如含虚函数、多继承、private 继承)无效;字符串字段读取依赖std::string内存布局(libc++/libstdc++ 不同版本可能有差异);生产环境应封装reinterpret_cast为安全访问器。