short

C++26 reflection runs on a compiler you already have

· english· audience: working-cpp· AI-generated, reviewed by Filip Sajdak

You have probably read that trying C++26 reflection means fetching the Bloomberg clang fork. That was true for a long time. It is not true now. GCC 16.1, released at the end of April, implements enough of P2996 to run real code behind one flag.

Daniel Lemire posted a small program that prints values alongside their types. It is a good test case because it uses three C++26 features at once:

#include <print>
#include <vector>
#include <meta>

template <class... Ts>
void print_all(Ts... ts) {
    template for (auto t : {ts...}) {
        std::println("{} = {}",
                     std::meta::display_string_of(^^decltype(t)),
                     t);
    }
}

int main() {
    print_all(1.2, "abc", 3, std::vector<int>({2,3}));
}

Those three features are:

  • an expansion statement over a heterogeneous pack
  • the reflection operator ^^
  • range formatting for the vector

Compile it with g++ -std=c++26 -freflection and it runs.

The header is <meta>, the standard spelling, not <experimental/meta>. GCC accepts only the former and the fork accepts both. Write <meta>.

The two implementations do not agree

Same source, same output structure, different strings:

GCC 16.1 clang-p2996
1.2 double double
"abc" const char* const char *
3 int int
the vector std::vector<int> vector<int, allocator<int>>

GCC qualifies the name and elides the defaulted allocator. The fork does neither, and spaces the pointer differently. Both are defensible renderings of the same type.

The practical consequence: do not parse what display_string_of returns. It is for humans reading diagnostics and log lines. If you need something stable, call identifier_of for the declared name. Comparing reflections also beats comparing their printed forms.

What still needs the fork

Not everything. GCC rejects a range that passes through a named constexpr local:

constexpr auto members = std::define_static_array(
    std::meta::nonstatic_data_members_of(^^T, ctx));
template for (constexpr auto m : members) { ... }   // GCC: 'members' is not a constant

Write the same expression inline in the template for and GCC takes it. That one has its own post, because the reduced case is short and the workaround is a one-line edit.

Beyond that, the demos in this series that were written against the fork run unchanged on GCC once the header is fixed. Three examples:

Why this matters more than it sounds

Reflection has had a reputation problem. “It is in C++26” and “you need a research fork to try it” are different statements. The second one stops people looking. A feature you can reach with a flag on the compiler your distribution ships is a feature people will actually experiment with.

GCC 16.2 arrived in August and Fedora 45 has an accepted change to make it the system toolchain. That puts a working reflection implementation on ordinary machines. A language feature stops being a conference topic at that point.


Sources: