Naming a constant makes GCC stop believing it is one
This compiles on GCC 16.1:
template for (constexpr auto m : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, ctx))) {
std::println(" {} = {}", std::meta::identifier_of(m), obj.[: m :]);
}
This does not:
constexpr auto members = std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, ctx));
template for (constexpr auto m : members) {
std::println(" {} = {}", std::meta::identifier_of(m), obj.[: m :]);
}
error: 'members' is not a constant
Same expression, same initializer, same constexpr. The only difference is that the second one has a name.
The matrix
| Range given as | GCC 16.1 | clang-p2996 |
|---|---|---|
named constexpr local |
rejected | accepted |
| identical expression inline | accepted | accepted |
Clang takes both spellings and prints the same output either way. Naming a constant expression does not stop it being one.
Why it is worth knowing rather than filing away
The named form is the one people write. You compute the member list once, give it a name that says what it is, and use it. Splitting a long expression out of a loop header is ordinary style. Here it turns a working program into a compile error. The message sounds as if your code is wrong.
The workaround is unpleasant in proportion. Inline the expression back into the template for. A reviewer would ask you to undo exactly that edit.
If you are porting reflection code written against clang, expect this shape of breakage. GCC has the feature. It disagrees about when a constexpr variable is usable as an expansion range.
A note on the reproducer
The version in the examples repo switches between the two forms with a macro. The first draft was wrong in a way worth repeating:
#if FORM == named // always true
Undefined identifiers evaluate to 0 in a preprocessor condition. So the test FORM == named is really 0 == 0 whichever way you set it, and both branches take the same path. It looked like GCC rejecting the workaround too. Instead of comparing a macro to a bare word, ask whether it is defined.
Sources:
- reduced from the splicing example in the reflection series
- P1306 “Expansion statements”
- P2996 “Reflection for C++26”
- both compilers on Compiler Explorer, GCC 16.1 and clang-p2996