第24章:反射
为什么有这一章
C++ 一直知道比你程序能问到的更多。编译器看到成员名字、字段类型、枚举常量、基类和布局。在 C++26 之前, 这些都不能作为值使用。你手写序列化器、维护宏列表,或者运行外部生成器。
C++26 反射改变了这一点。程序可以把类型或成员反射成 std::meta::info 值,用
consteval 函数查询它,并把结果拼回源码。同一个特性还能从一组反射成员补全一个
不完整聚合体。
这一章不假设你有任何反射基础。它从问题开始,讲 C++26 模型,写真实模式(枚举名字、字段访问器、 JSON、生成类型),最后讲编译器支持和采用策略。代码是 C++26,不是库模拟;最后一页讲你的编译器 还没有它时怎么办。
四页
简短版本
反射是编译期操作。^^ 把文法元素——类型、成员、枚举值、命名空间、变量——变成
std::meta::info 类型的反射值。std::meta 命名空间包含查询这些值的
consteval 函数:名字、类型、成员、基类、枚举值、布局、属性。拼接
[: r :] 把反射变回表达式、类型、作用域或模板参数。
template for 是展开语句。它在编译期展开循环,每次迭代有一个 constexpr
反射。define_aggregate 和 data_member_spec 从一组成员描述补全不完整
聚合体。这是特性的生成那一半。
心智模型有四步:用 ^^ 反射,用 std::meta 查询,用普通 if
和循环决定,然后拼接或生成。控制流是普通的 C++ constexpr 代码。只有数据类型和拼接语法是新的。
反射不替代模板,也不是运行时自省。它删除第二份声明:手写序列化器、枚举 switch、宏表、生成的包装。 它不删除策略。转义、可选字段、版本、未知键和错误处理仍然是设计决定。
C++26 有特性,但编译器还在追赶。GCC 16 有实现;Bloomberg 的 Clang 分支和 EDG 有实现;
写作时上游 Clang 和 MSVC 没有完整的公开实现。用 __cpp_impl_reflection、
__cpp_lib_reflection 和 __cpp_lib_define_static 保护特性,
保留回退,从最小、最划算的用例开始。
要点
- 反射把声明暴露成编译期值。
- 它是编译期内省,不是运行期类型系统。
- splicing 把反射值变回代码。
- 支持仍不完整;检查编译器和特性测试宏。