Skip to content(if available)orjump to list(if available)

C++26 Expansion Tricks

C++26 Expansion Tricks

40 comments

·March 22, 2025

Night_Thastus

I look at C++ code like this and feel like it's from a different planet. These are keywords and approaches I just never use myself. Templates ocassionally, but auto templates? std::meta? Whatever [:Member:] is? nonstatic_data_members_of? std::is_void_v? Plus the lambdas make this syntax even worse.

It feels like I'm looking at a conlang instead of C++.

Not hating the author, I'm sure they know their stuff and this is useful to someone. Just looking at this from the perspective of an ordinary C++ programmer, it looks very alien.

thechao

I tried pitching "generalized overloads" to Bjarne on a few occasions: `operator .` is highly problematic (you can read about it); but a number of the other `operator`s seem to be ok: `;`, `?:`, `if`, `for`, `while`, `switch-case`. Where things get challenging are: `goto-label` (just... mechanically). And, finally, do we want to overload aggregate & enumeration declaration? `typedef` declaration (this gets into name unification which is really awkward in templated contexts); function definitions, etc.). My argument was "leave that for C++14". For 90% of the code I was looking at the statement-level `operator`s solved the problems I had: it promoted the body of the functions into plain-old-template metaprogramming. Templated return types (a la Veldhuizen) converted function definitions into first class types that let me operate on functions, just like we've always done.

lpapez

Thankfully I no longer work with C++, but such proposals were my main problem with the language.

I had the impression that the vast majority of us regular users just wanted quality of life improvements: better error messages, networking library, a standard package manager etc. Literally most of what C++ code styles are concerned with is about picking what features are banned in that particular style.

But instead of doing something pratical, the commitee always went with the route of adding increasingly niche features citing that useful things are "out of scope" or "implementation defined" or "userspace".

So what you get is incompatible ecosystems and cultures for an extremely complicated language.

Very happy working with Go presently, where exactly the opposite approach is being taken by maintainers. Eg: There was huge pushback regarding generics introduction (a genuinely useful language feature IMO), but they introduced very good vulnerability scanning (govulncheck) without me even hearing about it being under development.

gpderetta

People want better error messages, networking, a package manager, etc. But they also want compile time reflection. Sometimes it is even the same people!

jryan49

Yeah look at this eye watering line:

    using ret_t = first_non_void<decltype(fnc.template operator()<Elts>())...>;

dataflow

Of all the C++ complexities that's not the one I would complain about. All it's doing is getting the first non-void declared return type of the operator() invocations, each specialized with the Elts template parameter. It reads basically as you'd write it in English. There's much more eye-watering stuff out there!

null

[deleted]

greatgib

For me the following one is insane:

constexpr void operator>>(F fnc) const { (fnc.template operator()<Elts>(), ...); }

gpderetta

I think that the choice of overloading operator>> is from the blog author though, it is not from the introspection proposal.

Aardwolf

To me it's the ^^, like in

``` void print_members(T const& obj) { [:expand(nonstatic_data_members_of(^^T)):] ```

What is this ^^?

pjmlp

It was supposed to be only one ^ however it conflicts with Objective-C blocks, so they ended up settling on two ^^.

However many of us would have prefered reflect(T), but again many on WG21 seem to use notepad for programming and don't like to type.

It means the reflection data of the type.

saelthavron

> it conflicts with Objective-C blocks

Why does that matter?

> many of us would have prefered reflect(T), but again many on WG21 seem to use notepad for programming and don't like to type.

I don't know about anyone else, but I find reflect(T) much easier to type than ^^T. I have a high error rate on hitting 6. Not only that I have to move my hand from the home row to hit it.

null

[deleted]

misnome

> however it conflicts with Objective-C blocks, so they ended up settling on two ^^.

Which… were never added to C++, right? What a mess.

edflsafoiewq

It's the reflection operator. Reflection is supposed to be part of the yet-unreleased C++26, so it's not surprising you don't know it.

greatgib

Good news, c++ and brainfuck languages will soon be able to merge in a single language :-)

vinkelhake

You're looking at constructs mostly related to C++26 reflection. It's very new, so that might be why it looks like it's from a different planet. They necessarily had to invent some new syntax for it.

I think C++26's reflection is likely to be one of the most important changes to the language in a very long time.

tsimionescu

Did they have to invent new syntax for it? All other languages that have reflection have regular function calls for it. Why do ^^X instead of reflect(X)?

gpderetta

Not very many languages have compile time reflection. This is more like macros and syntax splicing.

Still a reflect keyword could have worked. Some argued that in reflection heavy code you would reflect a lot and the keyword would be a lot of noise.

I don't know if the reflection papers have been voted in already (I forgot how to check), so the syntax might still be up for debate.

edflsafoiewq

According to https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p29...

> The original TS landed on reflexpr(...) as the syntax to reflect source constructs and [P1240R0] adopted that syntax as well. As more examples were discussed, it became clear that that syntax was both (a) too “heavy” and (b) insufficiently distinct from a function call. SG7 eventually agreed upon the prefix ^ operator. The “upward arrow” interpretation of the caret matches the “lift” or “raise” verbs that are sometimes used to describe the reflection operation in other contexts.

Wumpnot

It is using c++ 26 features, so of course it looks slightly alien, nobody uses it yet.

spacedcowboy

To me, this seems like it increases cognitive load, which is the opposite of what it ought to be. At some point the abstraction is so abstract, that you have to stop and think about what the [redacted] you're actually reading, without it just making intrinsic sense.

If you want to enumerate over a bunch of items to print them out, do that. Don't wrap it up in some obscure language feature.

Stuff like this is why, of the two main "C with classes" extension languages, I prefer ObjC over C++. If I really need the speed of C++, I can just use it, but you'd better believe it'll be straightforward C++/STL. At all other times, it's easier to grok ObjC, easier to work with it, and (again) the cognitive load over and above 'C' is really minimal compared to C++.

I guess I'm just one of the old-fashioned folks who think that just because something was difficult to write, it shouldn't be difficult to read. Code clarity wins over brevity in my book any day.

jlarocco

> I guess I'm just one of the old-fashioned folks who think that just because something was difficult to write, it shouldn't be difficult to read. Code clarity wins over brevity in my book any day.

I treat template metaprogramming in C++ like macro code in Lisp - it's not something I use all the time and it's often more complicated than every day code, but used well it make the rest of the codebase simpler to understand.

The most complicated template I've written recently wraps a vendor's clunky old iterator classes (unrelated to STL) with .Current() and .Next() methods, and lets us use them in C++11's `for (auto thing : VendorIterator)` loops. In the vendor's defense, they started writing the library in the early 90s, before the STL was formally part of C++ - though it would be nice if they provided a template like this themselves.

In any case, the template greatly simplifies using the iterators (5 lines of code goes to 1) in dozens of places, at the cost of a complicated template that rarely needs to change. To me it's a fair tradeoff.

pjmlp

Objective-C is also full of warts, from its original days, the transition to be more developer friendly, special constructs to ease interop with Swift.

Also, had it not been for NeXT's reverse acquisition of Apple it would have been long forgotten by now.

danielscrubs

Swift as a language is great. But the compile time performance and the timeouts are just laughable, truly worthy of damnation.

pjmlp

Yep, a mix of type system complexity, and LLVM.

waynecochran

Cognitive load is greatly increased. As a corollary this kind of stuff becomes difficult to prove correct. There is a fine line between clever and stupid. Some of these C++ additions have jumped the shark.

greenavocado

I'm tracking all G++ 14+ flags and warnings that reduce the likelihood of writing unsafe or incorrect C++ code. What am I missing?

    # Primary Warning Set
    -Wall -Wextra

    # Enhanced Warning Sets
    -Wpedantic -Weffc++

    # Type Safety
    -Wshadow -Wold-style-cast -Wcast-align -Wconversion -Wsign-conversion 
    -Wdouble-promotion -Wimplicit-fallthrough=5 -Wlogical-op -Wuseless-cast
    -Wlifetime -Wsuggest-final-types -Wsuggest-final-methods -Wzero-as-null-pointer-constant

    # C++ Features
    -Wcatch-value=3 -Wctad-maybe-unsupported -Wdeprecated-copy-dtor

    # Memory and Object Safety
    -Wdangling-reference -Wpessimizing-move -Wredundant-move -Wclass-conversion
    -Wuse-after-free=3 -Wmismatched-new-delete

    # Declaration Consistency
    -Wmismatched-tags -Wredundant-tags -Wredundant-decls -Wnon-template-friend
    -Wattribute-alias=2

    # Advanced Analysis
    -Wstrict-aliasing=3 -Wstrict-overflow=5 -Warray-bounds=2 -Wzero-length-bounds
    -Wnrvo -Winterference-size

    # Attribute Suggestions
    -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=noreturn 
    -Wmissing-noreturn

    # Miscellaneous
    -Warith-conversion -Wundef -faligned-new

    # Static Analysis
    -fanalyzer -Wanalyzer-too-complex

dataflow

> (fnc.template operator()<Elts>() && ...);

Better not to do this. Cast the operand to void first. Otherwise if someone overloads operator, (the wisdom of that is another matter) it will affect the behavior.

gpderetta

If they do, they get to debug the resulting million lines template error. That will teach them :D.

indigoabstract

Is this a preview of things to come or just macho template programming?

It seems very esoteric, hard to understand.

Some of the examples don't even compile, but in any case, this isn't the working man's C++.

Maxatar

It's a preview of macho template programming.