← Back to Blogs
HN Story

The Real Cost of C++26 Reflection: A Benchmark of Enum-to-String Conversion

May 15, 2026

The Real Cost of C++26 Reflection: A Benchmark of Enum-to-String Conversion

Enum-to-string conversion is often described as the "hello world" of reflection. While it seems trivial, it is a fundamental requirement for logging, serialization, and debugging in professional C++ projects. With the release of GCC 16 and the introduction of C++26 reflection, developers now have a native way to handle this without the boilerplate of the past.

However, the primary concern for any C++ developer adopting new language features is the impact on build times. A recent benchmark by Vittorio Romeo explores whether the native reflection in C++26 is a viable replacement for "the old ways," specifically X-macros and template-based parsing tricks.

Three Approaches to Enum-to-String

To measure the cost, three distinct implementations were benchmarked using GCC 16.1.1 on a high-performance setup (i9-13900K).

1. C++26 Reflection

Using the new <meta> header, this approach is idiomatic and requires no macros or boilerplate. It uses std::meta::enumerators_of to iterate through the enum values at compile time.

2. Enchantum (C++17)

enchantum is a header-only library that uses __PRETTY_FUNCTION__ parsing tricks to derive enum names. It provides a clean call site but relies on scanning a configured range of values to find matches.

3. X-Macros (The Preprocessor Way)

This C-style approach defines a list of enumerators in a macro. A single macro expansion then generates both the enum class definition and a to_string function containing a switch statement. This was tested in two variants: one returning std::string_view and one returning a raw const char* to eliminate standard library dependencies.

The Benchmark Results

The benchmarks varied the number of enumerators (N) from 4 to 1024 to observe how compile time scales.

Total Per-Translation Unit (TU) Compile Time

N X-macro (const char*) X-macro (string_view) enchantum Reflection
Baseline 25.8 ms 25.7 ms 25.8 ms 25.7 ms
Header only 25.7 ms 136.0 ms 147.1 ms 180.8 ms
4 26.6 ms 137.6 ms 170.6 ms 186.7 ms
1024 54.7 ms 204.5 ms 272.0 ms 255.0 ms

Key Findings on Scaling

  • The X-macro (const char*) is the undisputed speed king. Because it avoids all standard library headers, it remains nearly at the baseline for small enums.
  • Reflection is asymptotically fast. The actual reflection algorithm costs approximately 0.07 ms per enumerator, which is nearly identical to the hand-rolled switch in the X-macro version (~0.06 ms).
  • enchantum scales differently. Its cost is tied to the configured scan range rather than the number of enumerators, meaning a small enum costs nearly as much as a medium one.

The "Header Tax"

The most significant insight from the data is that the perceived cost of reflection is not the reflection itself, but the inclusion of the <meta> header.

The cost of C++26 reflection is not the reflection. It’s <meta>.

Including <meta> adds a baseline overhead of approximately 155 ms per translation unit. In a large project with 500 TUs, this "header tax" can add nearly 80 seconds to a clean build. This highlights a recurring theme in C++: the most expensive part of compilation is often the standard library headers rather than the language features themselves.

Optimizing the Build: PCH vs. Modules

To mitigate the header tax, the benchmark tested Precompiled Headers (PCH) and C++20 Modules.

  • PCH (The Winner): Precompiling <meta> resulted in a 2.3x speedup, making reflection faster than both enchantum and the string_view X-macro variant.
  • Modules (The Surprise): In GCC 16, using modules actually slowed down compilation by approximately 2.2x compared to plain #include. The author notes this is likely a transient implementation detail of the current GCC 16 release, where the std module is a wrapper around a large header unit.

Practical Recommendations for Developers

For those integrating C++26 reflection into a production codebase, the following strategies are recommended:

  1. Prioritize PCH over Modules: Until module implementations mature, PCH is the most effective way to eliminate the <meta> overhead.
  2. Minimize Header Exposure: Do not include the enum-to-string header in other headers. Keep it in .cpp files to prevent the header tax from multiplying across the include graph.
  3. Stick to X-Macros for Ultra-Fast Builds: If your project requires sub-10-second clean builds, the overhead of <meta> is a non-starter. The raw const char* X-macro approach remains the most efficient.
  4. Caution for Library Authors: Be wary of exposing reflection in public headers. Forcing every consumer of your library to include <meta> can significantly degrade their build performance.

Community Perspectives

The discussion around these results highlights a divide in the C++ community. Some argue that compile-time cost is secondary to runtime performance and developer ergonomics. Others, coming from C or using custom code generators, argue that external build-step scripts (e.g., using Python or libclang) are more debuggable and offer infinite capabilities without bloating the compiler's workload.

Ultimately, C++26 reflection provides a massive ergonomic win, replacing ugly macro boilerplate with a type-safe, native mechanism. However, the "bill" for this convenience is paid in milliseconds per translation unit—a cost that can be managed, but not ignored.

References

HN Stories