Reflection in C++ refers to the usage of reflective programming, introduced to the C++ programming language in C++26. Reflection in C++, unlike other languages like Java and C#, occurs at compile time and incurs no runtime overhead.

Symbols in the C++ Standard Library handling reflection exist in the std::meta namespace, provided by the <meta> header.

As of currently, GCC has support for reflection, while support in Clang is undergoing development with an experimental Clang fork by Bloomberg implementing much of the specification. JetBrains's CLion IDE offers reflection support as of version 2026.2.

History

Until C++26, C++ was very limited in reflective capabilities. It featured type introspection via run-time type information, with a typeid operator for querying basic information at runtime through a type std::type_info. C++ also featured a dynamic_cast operator for casting a reference or pointer down to a more specific type in the class hierarchy, similar to a Java typecast.

C++11 introduced the <type_traits> header as part of C++'s initial move towards metaprogramming. This provided class templates allowing for checking whether a type satisfied certain traits (such as std::is_class, std::is_function, etc.), however could only act on types rather than any meta-object. These, however, provided features to constrain templates in template metaprogramming techniques, such as with "substitution failure is not an error" (SFINAE), which concepts simplified further in C++20 with a cleaner syntax to express type constraints.

Prior to the introduction of language-level reflection to C++, many third-party libraries attempted to emulate reflection-like capabilities. Among these include Boost.Hana for metaprogramming, reflect-cpp for data serialisation, POCO C++ Libraries for Java-style class loading and code generation, and Magic Enum for enum reflection. Qt features a meta-object compiler, qt-moc, for handling generating reflection metadata for Qt's C++ extensions.

The original technical specification for reflection, the Reflection TS, was published in March 2020 and was based on the C++20 standard. Being a type-based reflection system, it proposed an operator reflexpr() which queried a unique metadata type (i.e. reflexpr(A) and reflexpr(B) were the same if and only if A and B were the same type). It proposed the header <reflect> and namespace std::reflect, and classified type metadatas through concepts (such as std::reflect::Class, std::reflect::Namespace, std::reflect::Function). However, later proposals suggested value-based reflections rather than type-based reflections, to allow for greater flexibility.

Reflection, in its current form, was voted into the C++ standard in June 2025. It introduced the header <meta> and namespace std::meta. Rather than the type-based reflection seen in the Reflection TS, it introduced a value-based reflection system, where all reflection type metadatas were of a type std::meta::info, and queried by a new operator ^^. Many in the C++ Standards Committee highlighted its introduction as a major change to the language. In particular, Herb Sutter, chair of the ISO C++ Standards Committee and Hana Dusíková, assistant chair of the "Language Evolution" working group of the C++ Standards Committee, described the potential impacts for including reflection to C++ as a "whole new language." Sutter further described C++26 as the most 'compelling' release since C++11.

An additional proposal was made for consteval variables, for use with compile-time constructs like info, but was not added in C++26.

Herb Sutter additionally created a metaclass proposal building off of the features introduced in reflection, but it was not introduced in C++26. Code injection was also proposed for reflection, which would allow for token sequences to be injected into source code at compilation.

Overview

Reflection in C++26 is value-based. It introduces a new operator ^^, called the "reflection operator" (colloquially called the "cat-ears operator"), with type metadatas being represented as a type std::meta::info, which behaves as a mirror object, usually defined as decltype(^^int). It further introduces a "splice specifier" [: :], which can be seen as the "inverse" of the reflection operator ^^, taking a metadata information and turning it back to code. For example, if r reflects a member named name, obj.[:r:] becomes obj.name, while if clazz is ^^std::string, then the declaration typename [:clazz:] name = "John Doe"s; becomes string name = "John Doe"s;.

info is a scalar type, which defines equality and inequality, but not ordering. If it is default constructed, it produces a null reflection value, equal only to other null reflection values but no reflection values that refer to an entity. info is strictly consteval-only, meaning that it may exist only at compile-time and any expression with a consteval-only type may only be evaluated at compile-time.

While most entities and named symbols may be reflected, including functions, classes, enums, namespaces, others such as modules, cannot.

Parameter reflection, used for reflecting parameter types and parameter names, was introduced, allowing for advanced dependency injection techniques, language bindings, and Python-style "keyword-arguments".

C++26 introduces "expansion statements", written as template for. Building off of boost::hana::for_each()'s features, this loop is processed entirely by the compiler, and unrolls the loop for each item in the collection, such as a tuple, array, class, range, or brace-delimited list of expressions.

For example, the following demonstrates the usage of a expansion statement in an interface mixin with the curiously recurring template pattern to generate JSON from a struct.

Reflection uses exception-based error handling. C++26 introduces constexpr exceptions, allowing reflection-based exceptions to be thrown at compile-time. Functions in std::meta throw std::meta::exception, except for std::meta::define_aggregate() which causes a compilation error if invalid. Because std::meta::exception is primarily for handling invalid reflective operations at compile-time, in addition to the exception message, its constructor also takes a std::meta::info parameter for which metadata it failed on as well as an optional std::source_location parameter specifying the location of interest (by default, its current location).

Additional features for defining static strings, objects, and arrays were introduced for allowing for compile-time strings/objects/arrays to be promoted static storage for usage at runtime.

Annotations

Annotations allow for the addition of metadata to declarations and entities in such a way that can be read by reflection, implementing attribute-oriented programming. Historically, C++11 introduced attributes, which indicated information to the compiler, but these were either standard-defined or implementation-defined, and could not be custom-created. Additionally, reflection does not support querying attributes, and can be ignored by the compiler.

Thus, to distinguish from attributes, annotations are declared with similar syntax to attributes, but with an equals sign (=) before the annotation name. Unlike Java annotations, which have a unique syntax for declaration (@interface), C++ attributes may be any constant expression with structural type (even integers, like [[=1]]), but are typically declared as struct/class to allow for customisation. Annotations may be used either with a single global object (typically used for stateless/marker annotations), or may be constructed as needed (typically used for annotations needing to carry additional information). For example, using a custom annotation, a JSON serialiser can be informed to skip sensitive fields:

Annotations may be repeated per declaration, and may spread over multiple declarations. Additionally, for annotations Foo and Bar, [[=Foo, =Bar]] is equivalent to [[=Foo]] [[=Bar]] (annotations may appear in the same sequence.

Another use case is command-line argument parsing; annotations may be used for parser derivation, similar to that of the clap library in Rust, using constructible annotations:

Then, in use:

Comparison with other languages

C++ reflection, due to its static (compile-time) nature, incurs no runtime overhead or binary size increase. However, this also gives it less power than the runtime reflection systems of languages like Java (java.lang.reflect) and C# (System.Reflection), which are capable of loading and inspecting entirely new code at runtime. ^^X in C++ can be seen as similar to X.class in Java (which returns java.lang.Class<X>) or java.lang.Object::getClass() (which returns the wildcarded java.lang.Class<?>), or typeof(X) in C# (which returns System.Type). Unlike C++ where any constant expression with structural type may be an annotation, in Java, an attribute is declared as an interface using @interface, with additional specifiable information in java.lang.annotation. In C#, an attribute is any class that extends System.Attribute.

Meanwhile, compared to procedural macros in Rust, C++'s reflection is an actual reflection system, while Rust's procedural macros primarily handle code generation, as they act over Rust syntax. However, Rust procedural macros also provide support for custom attributes (like C++ annotations), with the #[attr] syntax for attribute macros and #[derive(Derive)] for derive macros.

See also