The Problem with C++ Templates
C++ has evolved as a language that encompasses many modern programming techniques. In this way, C++ has managed to stay relevant, despite the many issues that it may have compared to safer languages such as Java or C#. In C++, one can decide the set of features that will be used from within a large set of alternatives. If you’re inclined to do so, you could use only an strictly object oriented style, or maybe a functional oriented style, if that suits you better. Moreover, with the additions proposed in C++11, you can even use closures to make your coding style more functional.
Among the features provided by C++, one that it pioneered was the use of template programming. While templates provide some of the features allowed by Lisp macros, they also enforce type safety. This safety helps a lot when working with a language such as C++, which is so strict about the correct use of types.
Templates and the Type System
Templates have been crucial to provide some features previously missing in C++. For example, the idea of generic containers can only be possible with some kind of type instantiation. Even other languages such as Java and C# had to introduce template-like features in order to cope with the problem of generic containers.
While templates are very important, they also bring a severe burden into the language. Templates operate at compile time, making decisions about how to instantiate a particular type given the options allowed by the available template code. This kind of decision is not only powerful, but also time consuming.
The trouble with using templates more generally is that they are not very programmer friendly. While templates solve important problems, they are also hard to use and design. Here follow a few issues that I believe are of high importance.
Module Separation
C++ provides header files as a method for creating modules and explicitly separating interface from implementation. However, templates make this separation harder to achieve. When a compiler creates code from a template, it needs not only access to the interface, but also to the whole definition of the template. The reason is that much of the required information used by a template is stored in its definition. For example, what are the methods accessed in a particular type? These properties are needed by the compiler to decide if a template can be expanded or not.
In the past, there were some proposals to fix the module separation issue. Using the external keyword along with a template declaration was one of the options proposed by the ANSI committee, but it was never implemented by the major compilers, and nowadays it has been deprecated. It seems that there will be no solution in foreseeable future, so the only thing it remains for software engineers is to minimize the use of templates unless they are really required.
Compilation Time
Another issue that is a consequence of the above is the compilation time necessary to use templates. Since all of the template must be accessible to the compiler, if no additional calculations were necessary we would still have a severe burden by including templates in a project. However, remember that templates provide a sub-language within C++ that is known to be Turing-complete. This means that badly behaving templates can very well turn into infinite loops, among other issues that need to be debugged — at compile time. This means that using templates requires a lot of care from the part of the programmer, since it can result both in compile-time as well as run-time bugs.
But it doesn’t stop there. Templates also have a very particular way to bloat executables, which can turn a simple looking solution into a nightmare of slow linking times. Templates have this tendency to generate additional code for each instantiation for a concrete type. For example, for every compilation unit, in the first time you create a std::vectorthe compiler will try to instantiate yet another version of std::vector, even if there already is a version of that template for each compilation unit of the program. This is not such a huge problem because a linker will check all existing object files and remove duplicate versions of template instantiations. The real problem is that this process takes time. In a big C++ project this can consume from a few seconds to several minutes.
The other issue, however, happens when the method described above doesn’t remove all the multiple copies of a template. In fact, there might be thousands of legitimate instantiations of a template in a single program. For example, if you have thousands of types (which is common in a C++ project), you may also have thousands of different instantiations of std::vector, and even millions of instantiations of std::map(not common, but possible).
Then you have design issues that make this even worse. I once worked in a project that had a template for String, where n was the maximum size of the string. The designers of this template thought they were doing a good service, until you realize that there is a copy of this template for each of the hundreds of string sizes used in the project. Also, using a template like this induces the creation of related templates, so you end up having ImmutableString, etc.
Conclusion
I just scratched the surface of possible problems when using templates in C++. My goal is not to discourage the use of existing templates, such as the STL, but to caution against the indiscriminate use of templates in C++ projects. I personally think that you should use templates only to go around limitations in C++, because that is why they were originally created. Trying to use templates as a normal programming paradigm unleashes more problems, as we just mentioned, than it can possibly solve.
Further Reading
C++ Templates: The Complete Guide: I like this book because it covers all the information you need to become knowledgeable about templates. The book starts from the basics, but also discusses advanced techniques for template writers.
Similar Posts:
About the Author
Carlos Oliveira holds a PhD in Systems Engineering and Optimization from University of Florida. He works as a software engineer, with more than 10 years of experience in developing high performance, commercial and scientific applications in C++, Java, and Objective-C. His most Recent Book is Practical C++ Financial Programming.
Interesting, but you missed my greatest reason for using C++ templates: increased code abstraction and therefore maintainability. It is essential that I be able to maintain large C++ programs. That always boils down to being able to understand a piece of the code quickly enough to make a relevant change in a given amount of time. Any time I have similar, almost duplicate code, I look to use templates. This is because the resulting template reduces the footprint of the coding feature I am implementing (that is, increases write-once, use-many ratio). That, in turn, makes it more likely for me to accomplish my change task in the time I have. You have addressed seconds and minutes added in compiler time due to templates, but that is exactly what I want. I want compiler intelligence to be exercised while I sit back. What your article is missing is a feel for programmer time and what templates do for that. As I said, that is the only reason I sweat out and employ templates as much as possible.
By rtischer8277 on Nov 11, 2011
If you are conclusion is only that we should pause and think before always grabbing a template class, I agree (“only tool is a hammer” problem). And, yes, they can bloat your code (instrument for code coverage sometime and see the bazillion classes that don’t get touched, that you don’t even use); but honestly, what’s the alternative? Everyone writing their own string/set/vector/map/etc? That’s always worked out well, and, then the tradeoff for having lots of instantiations of a single class is lots of just slightly different classes, and unmaintainable code. I’ll take maintainable code any day. (Re: type safety – I’ll also take a template function over a macro, too.) I would not call it a “burden” but a tradeoff, and “severe” is overreaching by a long shot.
By KloderTX on Nov 11, 2011
Being maintenance-oriented, I reject the use of templates on two grounds not mentioned here:
1. Both the definition and the use of templates produce code with low legibility;
2. Debugging code dependent on templates is exceptionally difficult.
I have encountered no problem to which a template (or several) might apply that could not be equally well served by the appropriate use of classes.
By Francis W. Porretto on Nov 11, 2011
Rejecting templates out of hand is shortsighted, in my opinion. To do so, would imply rejecting the stl, which frankly I find ridiculous. I also like to make extensive use of boost in my projects, which is heavily template dependent.
As for the specific objections: 1) Template code is not necessarily harder to read than any other comlpex c++ code. Familiarity with the techniques is a major factor. 2) While I agree that it can be hard to find the “real” location of a template induced problem, my compiler does produce an “include stack” type of trace that will (inevitably) lead to the misuse of said template in my source.
I personally use a source file that “includes” a .cpp file which instantiates the templates my project specifically requires. That way, I get the benefit of templates, I can have the code definition in a .cpp file (the way I like it), know that I only get one instance of each class, and get faster compile times. This works for both VS 2010 and g++ (both of which I use and are requirements for me).
By Dominic Amann on Nov 11, 2011
On further digesting your blog-post, I would add that (as with anything else), proper development of templates requires a significant level of facility, not to mention expertise with c++ and generic programming in general. Proper use should not be so onerous (no-one complains about how dificult std::string is to use).
In your programming environment, the best and most experienced programmers that care to make their code usable by others would be good candidates for creating nay necessary template based code. This also holds true for fundamental classes in your project. Having neophytes or people without a good overall view of the project create templates is probably asking for trouble.
By Dominic Amann on Nov 11, 2011
@rtischer8277 maintainability has several dimensions, and using templates is just one of them. I would look for other alternatives first, such as classes for example, before trying to use templates.
By coliveira on Nov 12, 2011
@ KloderTX I am not against using templates, just think they may cause maintenance problems if overused.
By coliveira on Nov 12, 2011
@Francis W. Porretto I also share your point of view.
By coliveira on Nov 12, 2011
@Dominic Amann I think the STL is an example of what works with templates. The problem is that it is pretty hard to come up with a library as good as the STL. It takes much more work to develop template libraries than normal libraries.
By coliveira on Nov 12, 2011
This article partially misses the point of what is really wrong with C++ templates. See here: http://people.cs.uchicago.edu/~jacobm/pubs/templates.html for much better explanation.
A few important points:
1) it’s just macro system with very ugly syntax whose intention was to make it look like parametric polymorphism
2) templates are completely transparent for C++’s type system
3) C++ does not support functional style (mentioned in first paragraph) like Haskell or even Scala does (C++03 supports FP at the level of C, more or less)
By Tow dragon on Nov 12, 2011
I don’t find templates that horrible, to be honest. They allow rather nice approach to generic containers and algorithms which is really nice for projects that have many common elements that can be easily defined as templates.
Of course, templates have several (depends on how much you work with templates/hate C++) flaws but I never considered them to be so horrible. If you dislike putting everything into .h you can always extract implementation to .cpp file, which obviously requires you to manually instantiate all templates. But if you don’t use them so often, it won’t be such a problem.
If long compilation times are killing you, you can always play around with precompiled header which can help a bit.
As response to Tow dragon:
* Even though templates are a bit like macros, you won’t convince me that designing C-macro-style templates is easier. Even horrible compiler errors are better then dealing with buggy macros.
* Are templates really unaware of C++ type system? I mean, they are semi-macroish, but is there really no type safety, or don’t I get something?
By Bartosz Bielecki on Nov 13, 2011
Response to Bartosz:
* C macros are definitely worse than templates but Common Lisp / Scheme macros are much better. I think that when you need a macro, you virtually always need an AST rewriting macro.
* If you insist then yes, templates are a bit aware of the type system. When you instantiate a template, compiler checks if it is parametrized by type/template name/int literal, according to template argument list. That’s all. The rest of type errors are emitted from instantiated code, almost no different than non-templated code.
Real type safety is provided when the body of “generic” function/class is (at least partially) typechecked *before* instantiation. Even Java and C# are better than C++ in this case, not to mention the universal quantification of pure System F.
By Tow dragon on Nov 14, 2011
Interesting article, that is not, like many others, all black or white about the discussed feature.
I’d like to add some remarks to the added comments:
1°) About type safety: Because templates are kinds of meta-types, they require a kind of meta-type system in order to be totally safe. There was an attempt to add the notion of concepts checking to the C++ standard (proposed by Stroustrup himself, the father of the language), with the idea of declaring the properties (i.e. subtypes and methods) required from a template parameter in order to be able for the compiler to instantiate the template properly. It has been discarded recently by the Standard committee, for technical reasons, but the general idea lies on the right track. Nowadays, the compiler approximates it by attempting the instantiation and detecting incompatibilities afterwards (for example a missing operator not supported by the template argument type), but this generally results in a kilometric long and unreadable error message, plus the necessity to access to the whole template definition, as explained in the blog article.
The Boost library offers a viable alternative with the help of a clever library making use of various tricks to come close to the concept proposition, thus allowing to have missing-feature oriended error messages instead of cryptic ones. But this does not suppress all difficult error messages.
2°) As mentioned en passant in the article, the template system of C++ is actually Turing-complete, meaning that you can devise full compile-time programs in a lisp-like fashion. STL itself now makes use of it, and the Boost library does this intensively. Admittedly, designing such libraries is not for the faint of heart, but as a user you can usually expect that the library is already debugged for you (this is the idea of an external library in the first place), so the benefits to burden ratio is sufficiently high for everyday use, and, frankly speaking, the risk of stumbling on an infinite compilation loop is quite low, as every Boost use will confirm it.
This meta-programming facility is just what makes templates terribly useful in the C++ world. The generic container stuff constituted the rationale for introducing templates in the language 15 years ago, and all other “common” modern languages just do that, as mentioned, but they rarely offer more than just this primitive generic container possibility (already very usegul, I agree). The Turing-compleness available in C++ allows for a tremendous possibility: the incremental (and programmatically controlled) building of user-defined types at compile-time.
Taking an analogy, instead of buying a car by chosing some options (the simple and usual template instantiation mechanism), what you buy with modern C++ libraries is actually the whole car manufacturing plant facility, allowing you (the library user) to build as many different cars as you need (or like), with the added bonus that these generated types (eeeh cars) are lean and mean, that is they don’t contain any unecessary code, because the careful incremental construction won’t let unneeded features creep into the built type.
This may seem contradictory with the “code bloat” phenomenon explained in the article, but this isn’t, because we are not at the same level. And having a car manufaturing plant at hand can very well lead to your garden being filled with thousands of different cars with little differences from one to the next. The answer lies at two places: in the careful design of the library, that could for example very well optimize away the string[n] case mentioned in the article, and the careful use of the library by limiting the combinatorial explosion that could arise if template parameters are used without care.
Having a manufacturing plant under you control needs judgment, and you cannot have the full power without some risks.
But believe me, once you have tried this way of programming (i.e. à la Boost), you cannot step back anymore, because this opens you a whole new conceptual universe.
3°) A third, and very important, point that come to mind when speaking about STL and C++ templates is the functional shift that is allowed by STL-like libraries. After 20 years or so of having seen the world as a bunch of objects, you change again your viewpoint and recognize that algorithms are again the primary tool of choice, and the manipulated data (in the forms of objects, and templates, etc.) only comes second. Using the compile-time template instantiation mechanism allows you to make use of so-called “static (i.e. compile-time) polymorphism”, and the net effect of it is that you don’t need (or so rarely) virtual methods anymore!
This is not so say that you are not object-oriented anymore. Your are, but in a more elegant way, because your now totally type-safe poymorphism is resolved at compile-time!
And if you would like to add a pinch of lambda-calcul in your code, there are ways to do it (see for example the “spirit” library in Boost), although it is not as natural as in a language like Scala, where it is natively supported. Here, the library designers just made extensive use of the template machinery and operator overloading to extend the language in the functional direction. The following code snippet shows a simple example:
for_each( v.begin(), v.end(), cout << _1 << endl )
this is now valid C++ code with the "spirit" library.
These ideas are by the way not new. I already used a C++ library more than 15 years ago, called "dbTools.h++" from RogueWave, that allowed to design database SQL access queries by using only C++ syntax to combine various templates with redefined operators building an abstract syntax tree under the hood that was lazily (and automatically) resolved to the corresponding SQL query when you fetched the results (the trick involved redefining the assignment operator to trigger the actual query).
This way for a user of exploiting the template power of the language is very refreshing, and, believe me, I never encountered an infinite template loop in such cases, because the burden had already been removed by the library provider.
Remember this: having a F1 as a car allows you to run faster that your neighbour with his 2-chevaux, but you may need to be a better pilot to drive it, albeit perhaps not Alain Prost!
By NICOLET J.-D. on Nov 15, 2011
@NICOLET
Nice response, templates are definitely a valuable tool for C++ developers. The biggest cause of my reluctant relation to templates are bad experiences with Boost.Spirit. I was trying to use it to make moderately sized parser (about 30 rules) and I was plagued by cryptic, meaningless error messages. All I can do was to make voodoo programing-like changes in my code or learn this library enough to be able to develop it. As an example of what can be accomplished with with templates, it’s also an example of how hard it is to provide reasonable error messages for the user of templated library. So much trouble to conclude that Lisp had better macros fifty years ago…
@My previous response
I have forgotten about a few other examples of cooperation of templates and type system (overloading resolution, template argument deduction) but the conclusion remains the same: templates do not provide type safety.
By Tow dragon on Nov 15, 2011
Hello Carlos.
I am a novice dealing with computer software. Right now I am in the process of purchasing a template to build a website for my new business.
Can you tell me what is important to know before I move on? I will appreciate your comment very much!!
Thank you,
Ana
By Ana on Nov 13, 2013
I have noticed you don’t monetize your page, don’t waste your traffic, you
can earn additional cash every month. You can use the best adsense alternative
for any type of website (they approve all websites), for more
info simply search in gooogle: boorfe’s tips monetize your website
By BestCedric on Aug 19, 2018