Recently I decided to give the C++26 reflection proposal a try (clang fork from Bloomberg). I chose "AoS to SoA container" library as a pet project (Take a look if you're interested: [GitHub morfo](https://github.com/katkevich/morfo)). And here are my impressions.
## The dream of "finally we can get rid of template metaprogramming, and average C++ fella will be able to use C++26 reflection and constexpr metaprogramming instead".
My opinion is that this is far from being true.
Disclaimer: this is an opinion of a non-expect, but I would argue, a pretty advanced C++ user. So take it with a grain of salt.
As you may already know, one of C++ quirks is that it have multiple different "languages" within it: normal runtime C++, template metaprogramming, constexpr metaprogramming, and now reflection. To be fair, I've barely used constexpr metaprogramming before in my daily work or even in my pet projects, and I guess this is the case for the majority of C++ devs. I always had an impression that constexpr metaprogramming has a very limited usage scope in real world. But C++ reflection heavily rely on constexpr metaprogramming, so we must adapt.
The truth if that you still need to glue together your runtime with all these new shiny constexpr and reflection features. And if you want to generate code and use generated code at runtime (I would argue that the majority of cool use-cases of reflection are all about generating code) and not just evaluate a single constexpr value, you will need to use templates and
define_aggregate meta-function, coz templates IS the way we are generating the code now.What are the main traits of templates? Template arguments and variadics of course! Since we are talking about constexpr-based reflection your template arguments will be NTTP ones most of the time. And here lies the fundamental, most infuriating issue:
###
CONSTEXPR EVALUATION CONTEXT AND THE LACK OF GOOD SUPPORT FOR NTTP TEMPLATE ARGUMENTS in current C++. To be an NTTP argument your variable must be: 1. a
constexpr variable and 2. it has to be a structured type. So lets dive into these two statements. -
constexpr variable. This one is harder to achive as you may think. First of all, the fundamental quirk of constexpr evaluation/context is that simple local variable inside constexpr evaluation context IS NOT a constexpr variable. An argument of a
consteval function IS NOT a constexpr variable. Which means you cannot use it as NTTP or refactor you consteval function onto multiple smaller consteval functions (you're forced to pass it as NTTP which is not always possible because of NTTP restrictions). And you encounter this issue ALL THE TIME - you just write "your usual C++" consteval function (remember, this is our dream we aim for), but then suddenly you need this particular value inside of it to be constexpr 3 layers deep down the callstack... You refactor, make it constexpr (if you're lucky and you can do that) but then you realise that your for loop doesn't work anymore (coz you cannot have constexpr variable inside for loop), and you need to use template for loop instead. Also, you cannot use the addresses of constexpr variables (and iterators) which means you're range algorithms aren't always easy to use. And my guess that all of this won't change any time soon. Another thing is that when you ask something userful about your type using reflection proposal (nonstatic data members for instance) you always get
std::vector. And std::vector cannot be constexpr (at least for now, do we plan to fix that in future releases of C++?) so you can't use it as constexpr variable. Which means you cannot use it as NTTP. Same thing for standard containers as std::map or std::set. And even if we WILL be able to use standard containers in as constexpr variable will they be structured types?... "Allow me to retort, what about p3491 proposal which should fix that issue" you may ask. Well,