因为C++26的std::reflect尚未落地,当前标准(C++20/23)无原生编译期反射,所有方案均依赖宏、模板元编程或外部工具模拟,不可直接使用。

为什么不能直接用 std::reflect?
因为 C++26 的 std::reflect 还没落地,当前标准(C++20/23)里压根没有原生编译期反射支持。所有“编译期反射”方案都得靠宏、模板元编程或外部代码生成器模拟——这点必须先认清,否则后续设计全跑偏。
常见错误是试图用 auto 或运行时 typeid 去“假装”编译期反射,结果序列化字段漏掉、类型不匹配、或根本无法在 constexpr 上下文中使用。
- 宏方案(如
REFLECT_STRUCT(MyType, (int, x), (std::string, y)))最轻量,但需手动维护,且宏展开后调试困难 - Clang libTooling 或
reflexpr实验性扩展(如 MSVC 的/experimental:module+ 自定义 attribute)依赖特定编译器,不可移植 - 不推荐用
std::tuple手动打包字段:无法自动获取成员名,也不支持嵌套对象或访问控制(private成员)
怎么用宏 + 模板实现字段名/类型的编译期提取?
核心是让每个类型通过一个宏注册其字段信息,再用模板递归展开为可遍历的类型列表和名称列表。关键不是“反射”,而是“约定式描述”。
示例中,REFLECT_STRUCT 宏会展开为特化模板,把字段名存进 constexpr std::string_view 数组,把类型打包进 std::tuple:
#define REFLECT_STRUCT(Type, ...) \
template struct reflect_info<type> { \
static constexpr auto names = std::array{__VA_ARGS__}; \
using fields = decltype(std::make_tuple(__VA_ARGS__)); \
};
</type>
注意:__VA_ARGS__ 里不能直接写 (int, x)——这会触发宏解析错误。实际要用逗号分隔的字符串字面量 + 类型推导辅助,比如:
REFLECT_STRUCT(Person,
"id", "name", "score"
);
然后配合 std::get(obj) 和 names[I] 配对访问。这里容易踩的坑:
- 字段顺序必须和结构体内存布局严格一致(禁用
#pragma pack干扰) -
static_assert校验字段数与sizeof...(Args)匹配,否则编译失败但报错位置晦涩 - 不支持 bit-field、union、或含非默认构造函数的成员(除非显式提供
serialize_as适配器)
如何让 serialize 函数真正 constexpr 友好?
目标是让 serialize 能在 constexpr 上下文调用(例如初始化静态查找表)。这意味着所有分支、循环、字符串拼接都得是 constexpr 友好的。
关键限制:
- 不能用
std::string或std::vector:改用std::array+ 尾部空字符标记 - 不能用
for循环:改用std::index_sequence展开 - JSON 键名拼接必须用
constexpr字符串连接(如自定义concat_v模板变量)
示例片段(简化):
template <typename t size_t... i>
consteval auto serialize_impl(const T& obj, std::index_sequence<i...>) {
return concat_v...>,
": ",
to_string_v<decltype>(std::declval<t>()))>...,
"}"
>;
}
</t></decltype></i...></typename>
这里 to_string_v 必须为每种基础类型(int, double, bool)特化,且不能调用 std::to_string(非 constexpr)。
为什么嵌套对象序列化最容易崩?
当 Person 里有个 Address 成员,而 Address 也用了 REFLECT_STRUCT,此时 serialize 模板必须能递归进入 Address 的字段列表——但编译器不会自动推导嵌套深度。
解决方案只有两个:
- 强制要求所有嵌套类型也声明
reflect_info特化(无条件,不靠 SFINAE 推导) - 禁止直接序列化
std::vector等容器:改为要求用户显式提供serialize_container辅助函数,并传入元素类型的reflect_info
最常被忽略的一点:如果嵌套类型有 private 成员,宏展开时无法访问。必须要么改成 public,要么在类内加 friend struct reflector,否则编译失败提示像“‘x’ is private within this context”一样模糊。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











