C++标准不支持运行时反射,需用宏+静态注册+类型擦除实现伪反射;typeid仅提供mangled名且无字段信息,真正可用的是开发者显式声明的结构化元数据。
C++ 标准本身不支持运行时反射(如 Java 或 C# 那样通过
获取字段名、调用方法),但可以用宏 + 静态注册 + 类型擦除组合出一套轻量、可控的“伪反射”机制,适用于序列化、配置绑定、调试打印等场景。
为什么不能直接用
或
返回的是编译器生成的 mangled 名字(如
),不可读、不可靠、无字段信息;
也无法枚举类成员、获取字段偏移或类型元数据。真正需要的是开发者主动声明的结构化元数据。
用宏定义注册字段元信息(核心技巧)
关键思路:在类定义内部用宏展开出静态成员函数,返回该类型的字段描述表(
),每个
包含字段名、类型 ID、内存偏移、getter/setter 函数指针。
常见错误是宏里直接写
—— 这会导致编译失败,因为宏展开时不在成员函数上下文中。正确做法是用 lambda 或函数指针捕获偏移量。
立即学习
“
C++免费学习笔记(深入)
”;
实操建议:
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
定义统一宏
,接受字段列表,如
每个字段用
计算偏移,配合
转成
基址加法实现安全访问
类型标识不用
,改用枚举(如
,
)或
,避免 RTTI 依赖
宏必须在类
作用域
内使用,且类需为标准布局(
为 true),否则
行为未定义
手动注册与自动发现的取舍
没有编译器支持,就不存在“自动发现字段”。所有反射能力都依赖显式注册 —— 这不是缺陷,而是可控性的代价。
两种常见注册方式对比:
静态局部变量注册
:宏在类内部定义一个
,利用静态变量初始化顺序触发注册,但跨 TU 可能失效(ODR 问题)
显式调用注册函数
:如
,启动时集中调用,稳定可靠,适合大型项目
若启用 C++20,可用
+ 结构化绑定推导字段,但无法绕过“必须写宏声明”这一前提
一个最小可运行示例(C++17)
以下代码实现了字段名遍历和值读取,不含 setter 和嵌套对象,但已覆盖 80% 的配置/序列化需求:
define FIELD(type, name) { #name, std::type_index(typeid(type)), offsetof(THIS_TYPE, name) }
// 使用示例
struct Person {
int age;
std::string name;
};
// 手动注册(避免宏中无法推导 THIS_TYPE)
const std::vector Person::kFields = Person::BuildFields();
int main() {
Person p{42, "Alice"};
for (const auto& f : Reflectable::GetFields()) {
std::cout << f.name << ": ";
if (f.type == std::type_index(typeid(int))) {
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 << "\n";
}
}
注意
对非标准布局类型(如含虚函数、多继承、private 继承)无效;字符串字段读取依赖
内存布局(lib
c++
/libstdc++ 不同版本可能有差异);生产环境应封装
为安全访问器。
type_infotypeidstd::type_infostd::type_info::name()"St6vectorIiSaIiEE"typeidstd::vectorFieldMetathis->fieldREFLECTABLEREFLECTABLE((int, age), (std::string, name))offsetofstatic_castvoid*typeidTYPE_INTTYPE_STRINGstd::type_indexstd::is_standard_layout_voffsetofstatic const auto& reg = []{ ... }();ReflectRegistry::Register(&Person::GetMeta); consteval
#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::vector kFields; \
static const std::vector BuildFields() { \
return { __VA_ARGS__ }; \
} \
static_assert(true, "")
REFLECTABLE(
{ "age", std::type_index(typeid(int)), offsetof(Person, age) },
{ "name", std::type_index(typeid(std::string)), offsetof(Person, name) }
);offsetofstd::stringreinterpret_cast