Ch24: Reflection
Why This Chapter Exists
C++ has always known more about your program than your program can ask. The compiler sees the member names, the field types, the enum constants, the base classes and the layout. Before C++26, none of that was available as a value. You wrote the serializer by hand, maintained a macro list, or ran an external generator.
C++26 reflection changes that. A program can reflect a type or a member into a
std::meta::info value, query it with consteval functions, and splice
the result back into source code. The same feature can complete an incomplete aggregate from
a list of reflected members.
This chapter assumes no prior knowledge of reflection. It starts from the problem, teaches the C++26 model, writes real patterns (enum names, field visitors, JSON, generated types), and ends with the compiler support and adoption strategy. The code is C++26, not a library emulation; the last page explains what to do when your compiler does not have it yet.
The Four Pages
The Short Version
Reflection is a compile-time operation. ^^ turns a grammatical element — a type,
a member, an enumerator, a namespace, a variable — into a reflection value of type
std::meta::info. The std::meta namespace contains
consteval functions that query those values: names, types, members, bases,
enumerators, layout, properties. A splice [: r :] turns a reflection back into
an expression, a type, a scope or a template argument.
template for is the expansion statement. It unrolls a loop at compile time, with
a constexpr reflection per iteration. define_aggregate and
data_member_spec complete an incomplete aggregate from a list of member
descriptions. That is the generation half of the feature.
The mental model has four steps: reflect with ^^, query with
std::meta, decide with ordinary if and loops, splice or generate.
The control flow is normal C++ constexpr code. Only the data type and the splice syntax are
new.
Reflection does not replace templates, and it is not runtime introspection. It removes the second declaration: the hand-written serializer, the enum switch, the macro table, the generated wrapper. It does not remove policy. Escaping, optional fields, versions, unknown keys and error handling are still design decisions.
C++26 has the feature, but compilers are still catching up. GCC 16 has an implementation;
Bloomberg's Clang fork and EDG have implementations; upstream Clang and MSVC do not have a
complete public implementation at the time of writing. Guard the feature with
__cpp_impl_reflection, __cpp_lib_reflection and
__cpp_lib_define_static, keep a fallback, and start with the smallest use case
that pays for itself.
Key Takeaways
- Reflection exposes declarations as compile-time values.
- It is compile-time introspection, not a runtime type system.
- Splicing turns a reflected value back into code.
- Support is still partial; check the compiler and the feature-test macros.