C++26 reflection runs on a compiler you already have
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:
- Hinnant’s table verified by reflection
- a config file parsed at compile time
- filling a struct from regex captures
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:
- Daniel Lemire’s
print_allsnippet, which prompted this - P2996 “Reflection for C++26”
- GCC 16 release notes
- behaviour verified on Compiler Explorer with GCC 16.1 and clang-p2996