11 ms·
What's Wrong with C++ Templates? (2003)
- nemetroid 6y ago(2003)
- whimsicalism 6y agoMost important comment in the thread IMO.
- Hokusai 6y agoIn 2020 there are some replacements for what used to be templates and even templates have improved to be more expressive. 17 years after this article was written there seems to not be a better alternative. Templates are still at the core of C++ abstractions for the bad or the good.
- fwsgonzo 6y agoMuch less so with functions at compile time. The first thing I noticed was his example of the fibonacci numbers at compile-time. You can do those things with regular functions now, and just marking it as constexpr or constinit. I actually took the CRC32 algorithm and just slapped a template <int polynomial> and a constexpr on it, and called it CRC32 at compile-time. I'm using it for tons of string-hashing stuff where I want to replace a string with just a 32-bit number. It's incredibly efficient.
- layoutIfNeeded 6y agoHow does that look like in practice? I mean, you can’t use the table-based per-byte approach because you can’t store the lookup table with constexpr, so I guess you must be doing polynomial division via bit shifts and such, right?
- TeMPOraL 6y ago> you can’t store the lookup table with constexpr IIRC you can store a null-terminated string as constexpr, right? If so, then there's your lookup table.
- Google234 6y agoAlso I think absl has a constexpr map too.
- layoutIfNeeded 6y agoBut string literals are const, so you won’t be able to fill the lookup table based on the generator polynomial.
- fwsgonzo 6y agoThere is a table, here is the code: https://github.com/fwsGonzo/rvscript/blob/master/engine/script/util/crc32.hpp https://github.com/fwsGonzo/rvscript/blob/master/engine/scri...
- layoutIfNeeded 6y agoAh, true! I wasn’t aware that std::array was compatible with constexpr.
- sgerenser 6y agoI’ve done compile time crc32 string hashing for similar reasons with a raw array as a lookup table.
- tcbawo 6y agoIt seems that templates are becoming more of the core of C++ abstractions as the language picks up functional idioms and people are moving away from overusing/abusing object oriented designs. Concepts are making templates much accessible and easier to work with. Link time optimization helps reduce some of the template bloat and virtual call overhead. As much as people seem to be ragging on C++ lately, it keeps getting better and better.
- klipt 6y ago> 17 years after this article was written there seems to not be a better alternative Circle is a pretty cool alternative for C++ metaprogramming: https://www.circle-lang.org https://www.circle-lang.org it's just a prototype though, so who knows if it will ever become standard.
- tines 6y ago> For one thing, templates hurt the C++ compiler's ability to generate efficient code. This claim needs some support. C++ has notoriously slow compile times, but note that he's talking about efficiency of the code generated, not the efficiency of code generation itself, which I agree is damaged by templates. > It might surprise you to hear that, considering that C++ is "efficient" while functional languages like ML and Scheme are "inefficient," but it's true. C++ templates hide your intentions from the compiler: did you mean type abstraction? Did you mean a syntax extension? Did you mean constant-folding or compile-time code generation? The compiler doesn't know, so it has to just blindly apply the copy-and-paste strategy and then see what happens. I'm going to propose that this isn't a bug, it's a feature. The optimizer should be completely independent of syntax - and that's what C++ templates are, automatic AST expanders (plus some compile-time computation). All that the optimizer sees in the end are basically unrolled assembly instructions or IR, and we have battle-tested techniques to optimize this. Most of the heavy-duty optimizations in modern industrial compilers happen on the linear IR representation anyway. While templated code can be fatter than non-, link-time optimization can often reduce bloat when your your ad-hoc polymorphic template is really just acting as a parametric-polymorphic function a la ML. Now that I've played devil's advocate, someone more familiar with GHC, please tell me how Haskell has optimizations (e.g. deforestation? or is that just a solution for a problem that is specific to Haskell in the first place?) that make my assertions above untrue :)
- thaumasiotes 6y ago> C++ has notoriously slow compile times, but note that he's talking about efficiency of the code generated, not the efficiency of code generation itself, which I agree is damaged by templates. You're correct about the structure of the sentence, but note that people very commonly say something other than what they mean. Compare the rhetorical device of "transferred epithet": https://en.wikipedia.org/wiki/Hypallage https://en.wikipedia.org/wiki/Hypallage > a figure of speech in which the syntactic relationship between two terms is interchanged, or -- more frequently -- a modifier is syntactically linked to an item other than the one that it modifies semantically. >> "While he's waiting, Richard pops a nervous handful of salted nuts into his mouth." Who was nervous, the nuts or Richard?
- sreekotay 6y agoThe syntax is complicated and the debugging is messy. Said another way: writing is ugly, and reading is inscrutable. I'm not sure there's strong evidence (certainly none presented) about performance, etc
- barumi 6y ago> The syntax is complicated and the debugging is messy. Truth be told, the syntax is not complicated. What's complicated is how templates are abused to do stuff that wasn't identified in the feature's initial design requirements. Debugging is an implementation issue, not a language design issue. Templates are awesome. STL's generic containers are a blessing to the point where they can be used in practically all applications without the developer giving a second thought.
- samatman 6y agoIf you have an implementation issue which no implementation has managed to overcome, then you probably have a language design issue. I have very limited exposure to C++, so I'm not sure whether you're saying that some implementations have gotten this right. That isn't the impression I get as an outsider.
- barumi 6y ago> If you have an implementation issue which no implementation has managed to overcome I recall a time where GCC's error messages were infuriatingly cryptic. Then clang came up, and suddenly cryptic error messages were not only clear but helpful in identifying root causes. If your line of thought made sense, C++ would have been an awful language until clang fixed it. Don't confuse "implementation X didn't bothered to pay attention to a feature" with an issue in how a language was designed.
- secondcoming 6y agoVery true. clang seemed to give gcc a much needed kick up the arse. Even M$ are taking their C++ compiler, and standards adherence, seriously again.
- bluGill 6y agoFrom 2003. Templates and C++ have improved, as have compilers.
- beached_whale 6y agoThey have improved by a lot. My contexpr double(JSON) parsing in regular C++ is able to run with hand vectorized parsers. The compiler's autovectorizer is amazing.
- stingraycharles 6y agoThis post definitely needs [2003] in the title as it is fairly dated.
- glangdale 6y agoI came here to say the whole [2003] bit like everyone else, but it's worth noting how recent some of the "haha now c++ is better and this article no longer applies" changes are. C++ Concepts only made it into the standard in 2020.
- flqn 6y agoConcepts are a recent addition, but not the only one that has improved compile-time or static programming in C++. The `constexpr` specifier, for instance, was added in C++11 and with every release since then it's been able to replace more and more complicated template metaprograms with just regular C++ code. Other things like generic lambdas and return type deduction also serve to make template-heavy code simpler to both write and read.
- deleted 6y ago[deleted]
- rileymat2 6y agoI did not care much for C++ templates, until I moved to dynamic languages and understood the power. In dynamic languages I hated the inability to know if I was passing in an incompatible type. Templates are a perfect combination. The main problem with templates are the compiler error messages.
- ido 6y agoAs someone who rarely ever used c++ (and without templates when I did), what do they give you vs Generics like c# or java?
- giomasce 6y agoNot GP and I know some Java, but not C#. As I get it, in Java generics are little more than sugar coating: they basically check that your are not messing up with types. Having a List<String> is not very different (maybe not at all) from having a List<Object> and casting elements between Object and String when you put or get them, except that the compiler can check the types at compilation time. The List code is essentially the same. By contract, a std::vector<std::string> and a std::vector<int> in C++ are two completely different and unrelated classes. Any code they used is compiled twice, once for every value of the template parameter, and of course is optimized independently in each case. So, for example, although the template code is the same, the operation of copying a vector an give very different assembly codes: copying a vector of strings requires to call the copy constructor of each single string in the new vector, but copying a vector of ints basically reduces to calling memcpy() appropriately (thanks to all the appropriate abstractions, so that the compiler knows that to copy an int it is enough to copy its bytes, and this cannot fail due to an exception, and maybe other things). This is one of the great thing in C++ (at least, if you value this kind of optimizations), and it seems that OP misses the point entirely. It gives what is called (when it works) "zero-cost abstraction". You might like it or not, but it makes no sense to judge C++ design choices if you don't consider the ability to do zero-cost abstraction (which, for example, Java does not have).
- amelius 6y agoThe problem with templates is that it's a new language on top of an existing language. Why not use the same language for types as you already have for values?
- deleted 6y ago[deleted]
- glouwbug 6y agoIf anyone is interested, I'm in the process of back porting the STL to C99: https://github.com/glouw/ctl https://github.com/glouw/ctl The gist of its workings is including a header and defining its type: #define T int #include <vec.h> #define T float #include <lst.h> #define T str #include <deq.h> For more complex types, copy, free, and default constructors are supported, given they're defined before the inclusion of the container: typedef struct { void* data; } blob; blob blob_copy(blob* self) { ... } // Copy constructor void blob_free(blob* self) { ... } // Destructor blob blob_init(void* data) { ... } // Constructor blob blob_init_default(void) { ... } // Default constructor #define T blob #include <vec.h> int main(void) { vec_blob blobs = vec_blob_init(); vec_bloc_push_back(&blobs, blob_init(...)); // Invokes constructor, element 0, passed by value vec_blob_resize(&blobs, 42); // Invokes default constructor for elements 1-42. vec_blob copy = vec_blob_copy(&blobs); // Invokes copy constructor for elements 0-42. vec_blob_free(&blobs); // Invokes destructor for elements 0-42. vec_blob_free(©); } The motivation for starting this project was to provide C++ like functionality to C, but provide hugely reduced build times.
- dleslie 6y agoAs a hobby experiment I did something similar: ported generic containers and algorithms to C89. Unsurprisingly, what I found was that it was an uphill battle to replicate behaviour and performance that would be found under a better type system. Things like "aggregate values matching this predicate with this folding lambda into this target container" were clumsy as hell. But I like your approach of abusing preprocessor definitions prior to including the container header. Neat! https://github.com/dleslie/dl/tree/master/include https://github.com/dleslie/dl/tree/master/include https://github.com/dleslie/dl/blob/master/src/test_dl_algorithms.c https://github.com/dleslie/dl/blob/master/src/test_dl_algori...
- glouwbug 6y agoThe alternative would be to rely on void* for all data types, which was really nicely presented in Mastering Algorithms with C by Kyle Loudon, but after working through it the lack of type safety for even basic types was a huge pain. At least with a bit of pre-processor magic, type safety and contiguous data is promised. Also, getting crafty with precompiled headers, one could dump all the expansions into a single templates.h, compile a .gch, and reap the benefits of hundreds of containered types with instantaneous compile times.
- stephc_int13 6y agoMy main issue with template, aside from the slow compilation time, is that they are most of the time very difficult to read. And in my opinion, legibility is an extremely important feature when you're writing code that you plan to understand and maintain for years. Writting less code is a nice-to-have, but if the cost is that it's a complete mess, I'm out.
- GuB-42 6y agoSimple templates are not that hard to read. They can be tricky to write if you have things like templates using templates and specialization. But once you get the compiler to understand what you want, reading the code is usually not a problem. Hell breaks loose when you start doing "metaprogramming". I did a bit of it and it felt like I was exploited the compiler in clever ways not intended by the original authors, SFINAE checks in particular. And to be honest, I think it is the case, or some people should have their head checked. From the look of it, some modern C++ features attempt to fix that mess, notably concepts in C++20.
- deleted 6y ago[deleted]
- Const-me 6y ago> one is writing code that is abstracted over types, and another is a clever trick called template metaprogramming I don't use either of these tricks much. Metaprogramming is especially bad, pretty much unreadable once written. There's a third useful thing people do with templates - pass integer numbers to compiler. Unlike meta-programming, doesn't hurt readability. There're instructions like _mm_shuffle_ps or _mm_srli_epi16 or _mm_extract_epi32 which encode an integer into machine codes. Instructions can encode memory references like `ptr [rax+32]`. And even without low-level trickery, it's sometimes useful to compile the same code multiple times, with changes somewhere. Templates is an easy way to express that in C++, here's an example: https://stackoverflow.com/a/59495197/126995 https://stackoverflow.com/a/59495197/126995. A nice related feature is `if constexpr` from C++/17, such branches are resolved at compile-time and therefore free.