I don't have a source, but I have read that the reason this wasn't originally introduced into C++ was because they couldn't agree on what the syntax would be. So much pain because the committee is dysfunctional, it took a decade plus to introduce something that everyone from the start wanted and understood would improve things. Par for the course with C++.
Yeah, but then look at C++ - does the syntax really look like it's "just right" to you? Modern JS features actually tend to have cleaner syntax, generally speaking.
Modern JS or any JS is such a different language with not even a tenth of the features that C++ has, it's ridiculous to compare them. It's no wonder it looks "cleaner" -- it just isn't doing anything like what C++ does.
I'm not sure that this is true. Let's take a look at generics. In C++, we use angled brackets like myFunction<SomeType>();
This could however be done without introducing angled brackets. myFunction(myFirstClassType); which is what more and more languages nowadays do like Zig.
> I don't have a source, but I have read that the reason this wasn't originally introduced into C++ was because they couldn't agree on what the syntax would be
I can totally imagine that. That's double ironic because the syntax is pretty obvious (which I find pretty surprising for a new C++ feature :)
But yeah, it's amazing that we only get this 15 years(!) after the introduction of variadic templates. I can totally see why some languages would decide against ISO standardization.
I usually find myself putting parameter packs into tuples and then using `std::get` on the tuple to index. It's not perfect (will end up making copies), but has the side benefit of being able to additionally store the parameter pack for later.
for constexpr( size_t I = 0; I < INPUT_COUNT; I++ )
SetupInput<I>(options, transport_manager, subscriber_queues[I], thread_pool, templated_topic_to_runtime_topic);
}
Yes, there's some syntax snags around it (right now I looks mutable, something like `for (constexpr size_t I : INPUT_COUNT)` might be better), but there has to be some sane middleground.
> right now I looks mutable, something like `for (constexpr size_t I : INPUT_COUNT)` might be better
I thought the same, but then you lose the ability to control the increment step. For example, one might want to iterate pairwise.
Regarding syntax, you could mandate that the loop variable has to be declared `constexpr` as well, which makes it clear that it can't be modified in the loop body.
It's been ages since I've done metaprogramming with C++. I had to look this up, since my first reaction was: just use recursion! Yeah, I can see why a for-loop is the preferred way to go here.
Turns out that const-call depth is limited to 512, but by implementation, not by the standard (which is undefined).
I don't find them painful, but they're definitely extremely weird looking. The fact that they're "fold expressions" but are normally used with operator comma to sequence together things that want to be statements is a pretty strong sign that something went wrong in the design process.
If you want to iterate over the whole thing you can just use a fold expression, which arguably look cleaner than the non-compiletime ways to map over a collection like std::transform.
Even with fold expressions, sometimes you need the actual index. See here https://github.com/basis-robotics/basis/blob/ca70aee6daca37c... - I did use a fold expression, but I needed the actual index in the fold expression to both do array access and to pass down into a template parameter.
Bro fold expressions are great and all but Jesus Christ the name spacing in that file. Like have you ever heard of this hidden C++ feature called `using namespace`?
Yes, and? It's much easier to remove namespaces later using `using namespaces` if it really becomes a maintenance hazard than to go through and add them all back. This is library code that was intended to be included with unknown customer code, part of the goal is to avoid namespace collisions. (The `basis::` prefixes should go, though, the entire file is inside `basis`)
> It's much easier to remove namespaces later using `using namespaces`
lol how? sed? that's not a "much easier", that's a hack. it's actually much easier to remove `using namespace` because then the compiler will catch resolution errors.
i actually didn't notice this was a header in which ok fine don't use `using namespace` but still this is java EE levels of boiler plate you've got going on;
Arguably is doing a lot of work here, peoples eyes tend to start glazing over when I tell them it's a parameter pack fold :) I'd much rather have a loop that doesn't confuse people not familiar with the syntax.
My understanding is that all of the functions found in the C compatibility headers that the C++ standard defines can not be made constexpr. sqrt is among them, as are a lot of the math functions.
The griping in the comments here is insane considering that being able to easily static_assert(first(1, 2, 3, 4, 5) == 1) is a pretty powerful language feature.
Zig's approach to generics and metaprogramming - treating functions and types as first-class compile-time values in the base language - makes more and more sense.
More insane... syntax from the C++ committee. How about a readable function-like keyword, like any other self-respecting language? C++ surpassed APL and Perl by far already. Really, in APL there is some consistency at least.
array[index] is insane syntax? That's all that's being talked about here (allowing array-like indexing of parameter packs which are variadic template parameters).
C++ has the loudest reactionaries to syntax changes of any language community out there. Something as simple as this has 10 complainers. Imagine if someone actually proposed something radical like rust's lifetime syntax? People would be rioting in the streets.
But also part of me thinks the people making these complaints don't actually write it (C++). Because if you do then you don't really care (you just pick it up eventually without thinking about it).
btw: you can actually write, for example, the first function as just:
auto first(auto... params) { return params...[0]; }
Unless I'm missing something - but the post is showing off the fact that you can index into the list of types of the parameters, as well as the parameters themselves (in which case you need full template syntax).
You don't strictly need it, but you get rid of a bunch of ambiguities in the grammar. Not that it'd solve all the ambiguities in C++.
Is a<b, c>(d) a function call or two comparisons combined with the stupidest operator in the language? Impossible to know without type information. (This is also why rust has the super ugly ::<T> for generics, so that it's unambiguous)
I like to code in cpp as a hobby (computer vision stuff) but avoid template programing like the plague. The part of std is already scary stuff with 50 lines of errors for single typo.
It's actually becoming easier as they make it a real programming language.
The infamous template error messages are generally a result of two things: first, too many candidate functions because logic is cobbled together out of a nest of SFINAE overloads, which is ameliorated by moving the logic into normal code with if constexpr, etc; and second, type errors occurring deep in the call/expansion stack because unchecked values keep getting passed on just like in a dynamically typed language (which TMP basically is), which is ameliorated by moving the type checks higher up in the stack with concepts or static_assert.
I personally quite like templates and constexpr. I don’t do anything too crazy with them, but I like being able to write code that will compile down to efficient code without runtime checks. I tried to write equally generic code in typescript recently and I had to make peace with the fact that I had to pay for either an indirect call through inheritance or a runtime conditional, where in C++ I could use template code and a constexpr if to choose the correct code path at compile time.
After C++17, template programming has become relatively easy.
You can use enable_if and static_assert, alongside type traits to check the types, and give useful error messages.
With C++20 you get concepts lite, which while not being as good as C++0x concepts, still simplify the code a lot, improve error messages, and remove the need for the classical tricks, when used alongside constexpr.
I code cpp professionally. But I still dread template programming. Pretty much a guarantee that I'm going to end up spending an afternoon wading through horrifyingly inscrutable error messages.
50? That's it!? I work in a code base where a previous developer went nuts with overloads and I routinely get hundreds of lines of output for a single compiler error.
But how are you writing generic code then? C preprocessor macros? Abstract classes? No genericity at all?
C preprocessor macros are a hack, they are useful, but they come with plenty of issues. If you are using macros, there is a good chance there is a superior solution that uses templates.
Abstract classes impact performance, sometimes significantly, and it doesn't solve all problems templates solve. If you don't care, then you would probably be better off doing Java (or languages in the same family) than C++.
If you are not using genericity at all, it may actually be a good thing! Too much abstraction is a common problem. But sometimes you need it. Refusing to use generics will limit what you can do. And if it is your job, it will probably limit your pay too.
50 lines? For std io I get 100s of lines of candidate suggestions. It's so dumb. I usually turn off the suggestions error. Only about 35 years as a C++ developer so maybe I will become less grumpy about template errors as I age.
I'm not necessarily going to defend proc macros, which have a lot of problems but are also better than the alternative in many languages (like Go's over-reliance on codegen). Macros-by-example are pretty nice though, even with the special syntax.
But in Rust, you don't need to delve into any kind of macro for polymorphism like you do in C++, since Rust has a real generics system. There is vastly more C++ code that instantiates templates than Rust code that uses macros.
I was a bit unclear there—I was referring to templates, not C++ macros. I believe that concepts in C++20 have improved the situation (although I haven't personally worked with a C++20 codebase and don't know how widely adopted it is in the ecosystem).
The only thing you may complain about are error messages due to duck typing at compile time gone wrong, and even that is kind of already sorted out in C++17.
C++17 example, showing polymorphism at compile time without macros.
Naturally checking for speak() existence with enable_if, static_assert and type traits could be added, though this is an example, and nowdays we would make use of concepts anyway.
Unless this is about some particular technique I've missed, isn't this what virtual methods are for? Only place I'm aware of where you see macros-based polymorphism is in C.
Virtual methods are runtime polymporphism. Templates are compile time polymorphism, and therefore typically are faster (more inlining + avoiding virtual function call).
It feels like programming in the last five years has turned into a competition of who can make the most abstract meta programming patterns. I think if you find yourself making use of this feature, you’re probably in a deep hole of your own making.
What is an example of a useful function that takes an arbitrary number of arguments each of which has an arbitrary type that isn’t as trivial as the examples given in the article? For super trivial stuff this is useful, and if std used this under the hood to provide std::first_arg or something, that would make sense. But the absence of this language feature doesn’t really seem like it’s holding back 99.9% of users.
People writing generic libraries need this. As an example, I've seen it used a lot in anything related to logging and monitoring, since these generally need the ability to take arbitrary data types and work with them in different ways. The same is true for database APIs, etc.,.
I do think as well that some deeper scrutiny of semantics with the tools of PL research might have helped ahead of time - some of the most glaring warts like the perfect forwarding problem, or the need for reference collapsing, would have been avoided if the standards committee had taken a more rigorous approach to understanding semantics and their implications. I hope Zig’s comptime will get that level of scrutiny.
It would have been a huge, sudden increment in complexity from absolute zero, causing it to be forever written off and ignored as an incomprehensible curiosity looking for a practical application.
You have to suck people in with, "hey, you can write these two similar C-like functions under one definition, the correct one of which is automatically deduced and used".
Because, how do you use it? How do you add debug printing to try and understand where the bug is? How do you debug even anything?
Like, suppose your concept has a composite requires statement that invokes a constexpr predicate of two derived types, and it is not "true" as you expected, so overload resolution picks the wrong function.
What is the analog of gdb to use in this situation?
I don't have a source, but I have read that the reason this wasn't originally introduced into C++ was because they couldn't agree on what the syntax would be. So much pain because the committee is dysfunctional, it took a decade plus to introduce something that everyone from the start wanted and understood would improve things. Par for the course with C++.
At least it's progress over the first decade of C++'s life where NOTHING happened.
Suppose c++ is around for 100 more years (i'll take the over).
Sacrificing a couple decades in order to get the syntax just right is worth it. Just look at... Javascript.
Yeah, but then look at C++ - does the syntax really look like it's "just right" to you? Modern JS features actually tend to have cleaner syntax, generally speaking.
You make a great point. It was a good intention that led to a bad outcome. Many such cases
Modern JS or any JS is such a different language with not even a tenth of the features that C++ has, it's ridiculous to compare them. It's no wonder it looks "cleaner" -- it just isn't doing anything like what C++ does.
We're talking about syntax, not semantics, so I don't see how it matters.
You can't express more semantics without more syntax. So the "clean" JS is not burdened by the extra semantics that C++ must account for.
I'm not sure that this is true. Let's take a look at generics. In C++, we use angled brackets like myFunction<SomeType>();
This could however be done without introducing angled brackets. myFunction(myFirstClassType); which is what more and more languages nowadays do like Zig.
> I don't have a source, but I have read that the reason this wasn't originally introduced into C++ was because they couldn't agree on what the syntax would be
I can totally imagine that. That's double ironic because the syntax is pretty obvious (which I find pretty surprising for a new C++ feature :)
But yeah, it's amazing that we only get this 15 years(!) after the introduction of variadic templates. I can totally see why some languages would decide against ISO standardization.
I usually find myself putting parameter packs into tuples and then using `std::get` on the tuple to index. It's not perfect (will end up making copies), but has the side benefit of being able to additionally store the parameter pack for later.
This is nice, but I'm still waiting for a constexpr for loop to use it with.
Pretty please.
would be a lot more readible as Yes, there's some syntax snags around it (right now I looks mutable, something like `for (constexpr size_t I : INPUT_COUNT)` might be better), but there has to be some sane middleground.P1061 is in C++26 so you can instead do:
yay!Oh, that is nice!
That's better, yeah. I still prefer plain ole for loops, but that's much better.
> right now I looks mutable, something like `for (constexpr size_t I : INPUT_COUNT)` might be better
I thought the same, but then you lose the ability to control the increment step. For example, one might want to iterate pairwise.
Regarding syntax, you could mandate that the loop variable has to be declared `constexpr` as well, which makes it clear that it can't be modified in the loop body.
You could make a temporary or use some std::range/span helper. Double constexpr would work, too. Feels gross in the same way template template does.
Meanwhile, in CL/Clojure: everything is constexpr with no syntax difference. You can even do I/O at macro-expansion time.
And I say that as someone using C++17 at work, not just a smug lisp weenie.
template for is proposed for 26 as part of the reflection papers.
It's been ages since I've done metaprogramming with C++. I had to look this up, since my first reaction was: just use recursion! Yeah, I can see why a for-loop is the preferred way to go here.
Turns out that const-call depth is limited to 512, but by implementation, not by the standard (which is undefined).
https://stackoverflow.com/a/9320911/713831
There's a way to "iterate" over parameter packs called fold expressions, but they're painful and look very weird.
I don't find them painful, but they're definitely extremely weird looking. The fact that they're "fold expressions" but are normally used with operator comma to sequence together things that want to be statements is a pretty strong sign that something went wrong in the design process.
When I first touched this around C++14 or so, I used recursion. It resulted in even worse code.
If you want to iterate over the whole thing you can just use a fold expression, which arguably look cleaner than the non-compiletime ways to map over a collection like std::transform.
Even with fold expressions, sometimes you need the actual index. See here https://github.com/basis-robotics/basis/blob/ca70aee6daca37c... - I did use a fold expression, but I needed the actual index in the fold expression to both do array access and to pass down into a template parameter.
Bro fold expressions are great and all but Jesus Christ the name spacing in that file. Like have you ever heard of this hidden C++ feature called `using namespace`?
Yes, and? It's much easier to remove namespaces later using `using namespaces` if it really becomes a maintenance hazard than to go through and add them all back. This is library code that was intended to be included with unknown customer code, part of the goal is to avoid namespace collisions. (The `basis::` prefixes should go, though, the entire file is inside `basis`)
> It's much easier to remove namespaces later using `using namespaces`
lol how? sed? that's not a "much easier", that's a hack. it's actually much easier to remove `using namespace` because then the compiler will catch resolution errors.
i actually didn't notice this was a header in which ok fine don't use `using namespace` but still this is java EE levels of boiler plate you've got going on;
this signature is 90% namespace resolution - i have to squint to actually find the types...Look I agree, it could be more readable. :)
Arguably is doing a lot of work here, peoples eyes tend to start glazing over when I tell them it's a parameter pack fold :) I'd much rather have a loop that doesn't confuse people not familiar with the syntax.
Can we have constexpr math functions like sqrt already?
Seems like since C++26, it does use constexpr https://en.cppreference.com/w/cpp/numeric/math/sqrt
My understanding is that all of the functions found in the C compatibility headers that the C++ standard defines can not be made constexpr. sqrt is among them, as are a lot of the math functions.
The compatibility can be solved with macros to toggle it on or off depending on how you're building. C++26 has constexpr sqrt.
There is a paper suggesting to replace all macro based code, due to way modules work.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p26...
The griping in the comments here is insane considering that being able to easily static_assert(first(1, 2, 3, 4, 5) == 1) is a pretty powerful language feature.
Zig's approach to generics and metaprogramming - treating functions and types as first-class compile-time values in the base language - makes more and more sense.
C++ parameter packs are really bad ass, super cool to see this added
More insane... syntax from the C++ committee. How about a readable function-like keyword, like any other self-respecting language? C++ surpassed APL and Perl by far already. Really, in APL there is some consistency at least.
array[index] is insane syntax? That's all that's being talked about here (allowing array-like indexing of parameter packs which are variadic template parameters).
C++ has the loudest reactionaries to syntax changes of any language community out there. Something as simple as this has 10 complainers. Imagine if someone actually proposed something radical like rust's lifetime syntax? People would be rioting in the streets.
But also part of me thinks the people making these complaints don't actually write it (C++). Because if you do then you don't really care (you just pick it up eventually without thinking about it).
btw: you can actually write, for example, the first function as just:
Unless I'm missing something - but the post is showing off the fact that you can index into the list of types of the parameters, as well as the parameters themselves (in which case you need full template syntax).What I said. ... as type and identifier suffix is just nonsense.
A proper language would have used ... to designate varargs, and a special identifier to hold these args. Just as the macro preprocessor.
Why would we need a function keyword?
You don't strictly need it, but you get rid of a bunch of ambiguities in the grammar. Not that it'd solve all the ambiguities in C++.
Is a<b, c>(d) a function call or two comparisons combined with the stupidest operator in the language? Impossible to know without type information. (This is also why rust has the super ugly ::<T> for generics, so that it's unambiguous)
C++ continues its long journey to making its template system an actual usable programming language.
I like to code in cpp as a hobby (computer vision stuff) but avoid template programing like the plague. The part of std is already scary stuff with 50 lines of errors for single typo.
It's actually becoming easier as they make it a real programming language.
The infamous template error messages are generally a result of two things: first, too many candidate functions because logic is cobbled together out of a nest of SFINAE overloads, which is ameliorated by moving the logic into normal code with if constexpr, etc; and second, type errors occurring deep in the call/expansion stack because unchecked values keep getting passed on just like in a dynamically typed language (which TMP basically is), which is ameliorated by moving the type checks higher up in the stack with concepts or static_assert.
I personally quite like templates and constexpr. I don’t do anything too crazy with them, but I like being able to write code that will compile down to efficient code without runtime checks. I tried to write equally generic code in typescript recently and I had to make peace with the fact that I had to pay for either an indirect call through inheritance or a runtime conditional, where in C++ I could use template code and a constexpr if to choose the correct code path at compile time.
After C++17, template programming has become relatively easy.
You can use enable_if and static_assert, alongside type traits to check the types, and give useful error messages.
With C++20 you get concepts lite, which while not being as good as C++0x concepts, still simplify the code a lot, improve error messages, and remove the need for the classical tricks, when used alongside constexpr.
No need to go crazy with them.
I code cpp professionally. But I still dread template programming. Pretty much a guarantee that I'm going to end up spending an afternoon wading through horrifyingly inscrutable error messages.
> 50 lines of errors for single typo
50? That's it!? I work in a code base where a previous developer went nuts with overloads and I routinely get hundreds of lines of output for a single compiler error.
>The part of std is already scary stuff with 50 lines of errors for single typo.
IDE's make this less of a problem.
I also refuse to touch templates. Never needed them, never liked them. When I work on code that utilizes templates in c++, it reaffirms this feeling.
But how are you writing generic code then? C preprocessor macros? Abstract classes? No genericity at all?
C preprocessor macros are a hack, they are useful, but they come with plenty of issues. If you are using macros, there is a good chance there is a superior solution that uses templates.
Abstract classes impact performance, sometimes significantly, and it doesn't solve all problems templates solve. If you don't care, then you would probably be better off doing Java (or languages in the same family) than C++.
If you are not using genericity at all, it may actually be a good thing! Too much abstraction is a common problem. But sometimes you need it. Refusing to use generics will limit what you can do. And if it is your job, it will probably limit your pay too.
I hope programming isn't your day job.
Save often, compile often
50 lines? For std io I get 100s of lines of candidate suggestions. It's so dumb. I usually turn off the suggestions error. Only about 35 years as a C++ developer so maybe I will become less grumpy about template errors as I age.
Which I actually find more approachable than how Rust deals with two macros systems, each with its own mini language.
At least, as convuluted as C++ may be, template and compile time programming still rely on C++.
I'm not necessarily going to defend proc macros, which have a lot of problems but are also better than the alternative in many languages (like Go's over-reliance on codegen). Macros-by-example are pretty nice though, even with the special syntax.
But in Rust, you don't need to delve into any kind of macro for polymorphism like you do in C++, since Rust has a real generics system. There is vastly more C++ code that instantiates templates than Rust code that uses macros.
There are enough ways to do polymorphism in C++ without touching macros.
Given that Rust IO and many crate attributes are macro based, not sure who wins out.
I was a bit unclear there—I was referring to templates, not C++ macros. I believe that concepts in C++20 have improved the situation (although I haven't personally worked with a C++20 codebase and don't know how widely adopted it is in the ecosystem).
Still there is no need for macros.
The only thing you may complain about are error messages due to duck typing at compile time gone wrong, and even that is kind of already sorted out in C++17.
Anyway, C++ today means C++23.
C++17 example, showing polymorphism at compile time without macros.
Naturally checking for speak() existence with enable_if, static_assert and type traits could be added, though this is an example, and nowdays we would make use of concepts anyway.
Available at https://godbolt.org/z/WWfx5jWfMIn modern C++, you don't need macros for polymorphism at all
Unless this is about some particular technique I've missed, isn't this what virtual methods are for? Only place I'm aware of where you see macros-based polymorphism is in C.
Virtual methods are runtime polymporphism. Templates are compile time polymorphism, and therefore typically are faster (more inlining + avoiding virtual function call).
No need for macros in compile time polymorphism.
Am I crazy for thinking this is (mostly) useless?
It feels like programming in the last five years has turned into a competition of who can make the most abstract meta programming patterns. I think if you find yourself making use of this feature, you’re probably in a deep hole of your own making.
What is an example of a useful function that takes an arbitrary number of arguments each of which has an arbitrary type that isn’t as trivial as the examples given in the article? For super trivial stuff this is useful, and if std used this under the hood to provide std::first_arg or something, that would make sense. But the absence of this language feature doesn’t really seem like it’s holding back 99.9% of users.
People writing generic libraries need this. As an example, I've seen it used a lot in anything related to logging and monitoring, since these generally need the ability to take arbitrary data types and work with them in different ways. The same is true for database APIs, etc.,.
std::print is one. You need variadic generics to make it work.
It's unfortunate that it had to happen by accident, and now new features are reluctantly being pushed through piecemeal.
Imagine what it could have been if it was designed for programmability from the start.
> Imagine what it could have been if it was designed for programmability from the start.
Circle C++ compiler was designed for that: https://www.circle-lang.org/quickref.html
I do think as well that some deeper scrutiny of semantics with the tools of PL research might have helped ahead of time - some of the most glaring warts like the perfect forwarding problem, or the need for reference collapsing, would have been avoided if the standards committee had taken a more rigorous approach to understanding semantics and their implications. I hope Zig’s comptime will get that level of scrutiny.
It would have been a huge, sudden increment in complexity from absolute zero, causing it to be forever written off and ignored as an incomprehensible curiosity looking for a practical application.
You have to suck people in with, "hey, you can write these two similar C-like functions under one definition, the correct one of which is automatically deduced and used".
If this was sarcasm and I missed it, I apologize.
Because, how do you use it? How do you add debug printing to try and understand where the bug is? How do you debug even anything?
Like, suppose your concept has a composite requires statement that invokes a constexpr predicate of two derived types, and it is not "true" as you expected, so overload resolution picks the wrong function.
What is the analog of gdb to use in this situation?
lol
C with STL would've saved the world a lot of trouble.
> just write a half-assed code generator tool that uses void* under the hood, bro
Not the kind of "simplicity" I want. Stay in your reservation, please.
[dead]