Templates

Part of C++ FQA Lite. To see the original answers, follow the FAQ links.

This page is about C++ templates, one of the largest cannons to aim at your feet that the C++ arsenal has to offer. Templates solve the problems with C macros by creating 2 orders of magnitude more problems.

[35.1] What's the idea behind templates?

FAQ: A template describes how to build definitions (classes or functions) which are basically the same.

One application is type-safe containers; there are many, many more.

FQA: Let's get a bit more specific. The FAQ's answer is applicable to C macros, Lisp macros, ML functors, functions like eval found in many interpreted languages, OS code that generates assembly instructions used to handle interrupts at run time, and just plain code generation (writing programs that print source code). The purpose of all such devices is meta-programming - writing code that works with code, creating pieces of code which are "basically the same" (after all, they are built from the same rules), and yet have some interesting differences. The question is, how do we specify these rules and these differences?

The approach used in C++ templates is to use integral constants and types to represent the differences, and to use class & function definitions to represent the rules. The first decision prevents you from generating code dynamically, because the parameters can only be compile-time entities. The second decision prevents almost everything else, because you don't get to use a programming language to generate code - the only thing you can do is write code with some things factored out and made parameters. You can't do as simple and useful a set of "basically the same" classes as automatically generated class wrappers for remote procedure calls instead of normal "local" function calls (called "proxies and stubs" in COM terminology; there are many other terms). Even computing the factorial of an integer parameter is done using so much code abusing the language mechanisms that people with no useful work to do are proud of being able to accomplish this.

Beyond those fundamental limitations, templates follow the tradition of C++ features of interacting poorly with each other. Templates can't be compiled because they are not code - they are, well, templates from which code can be generated once you have the parameters, and then you can compile it. C++, like C, defines no way to locate the compiled code of a definition given its name. Consequently, template definitions are placed in #include files, and recompiled in each translation unit each time they are instantiated, even if the exact same instantiation is used in N other files. This problem is amplified by the tremendous complexity of the C++ grammar (the most complicated part of it is probably templates themselves), making this recompilation very slow. If your code doesn't compile, you get cryptic error messages. If it does compile, you might wonder what it means. That's where the interactions of the C++ type system (pointers, arrays, references, constants, literals...), function & operator overload resolution, function & class template specialization selection, built-in and user-defined implicit conversions, argument-dependent name look-up, namespaces, inheritance, dynamic binding and other things kick in. The sheer length of this list should be convincing: neither a human nor a program (say, an IDE) has a chance against this unprecedented syntactic power.

Poor support for meta-programming is not necessarily a very big deal, because you can do lots and lots of things without it. That is, unless you work in C++. For example, there are no built-in lists or dictionaries in C++; the standard library provides templates you can use, so you can recompile the definition of each kind of dictionary each time you use it in a source file. In fact, most of the code in the C++ standard library belongs to a conceptually and historically separate library called STL, which stands for "Standard Template Library". For example, that's where std::vector, which the FAQ recommends to use instead of the evil C arrays, comes from.

If you use C++, chances are that you're going to deal a lot with its obscure meta-programming facilities.

[35.2] What's the syntax / semantics for a "class template"?

FAQ: You add the parameters before the definition of your class, as in template<typename T> class Array { ... };, and then you can use the parameters in the definition of the class, as in T arr[N];, and then you can use your class by substituting the parameters as in Array<int> (the FAQ gives a code listing instead of using words; its example is as simple as that).

FQA: Wow, that sounds easy! Too bad it's wrong, in two ways.

First, things will not follow this straight-forward model - the FAQ itself discusses a couple of cases, like the need to resort to the typename keyword and the look-up of "nondependent names". All such cases illustrate that you can't take the definition of a class, parameterize some things making it a template, and expect it to just work - it's way more tricky.

Second, what about the cases where nobody would ever write the classes generated from templates manually, but templates are still used because that's the only meta-programming facility offered by C++? Of course there are also C macros, which have some limitations templates don't have (and vice versa), and at least compile fast. But in the C++ community, macros are considered the most evil feature ever, and using them is treated as a sin somewhere between speeding and blasphemy. Anyway, a majority of all uses of templates "beyond type-safe containers" actually fall in this second category. Let's look at the previously mentioned compile-time factorial example, which is trivial compared to nifty stuff like type lists:

template<int N>
struct Factorial
{
  enum { value = N*Factorial<N-1>::value };
};
template<>
struct Factorial<0>
{
  enum { value = 1 };
};

This code generates N+1 classes in order to compute the factorial of N. Talk about "the syntax / semantics of class templates". This is equally disturbing to humans - because it makes so little sense - and to compilers, which internally represent each class using a sizable data structure. This compile time computation technique tends to take a lot of compile time. When your only tool is a hammer template, not only does every problem look like a nail - you also hammer it with 100 hammers.

Oh, and when you want to use a template, be kind with the C++ lexer (the part of the compiler converting text to "tokens" so that the parser can check whether they make meaningful statements). Code like vector<vector<int>>, which tries to declare an inefficient implementation of a 2D array, won't compile - you need a space before the two > characters. Their concatenation looks just like the right bitwise shift operator. This is one of the more harmless, albeit confusing, awkward interactions between C++ features.

[35.3] What's the syntax / semantics for a "function template"?

FAQ: Pretty similar to class templates, plus you can usually omit the template parameters - the compiler will figure them out from the function arguments. For instance, you can write a swap function for swapping two values of any type, be it integers, strings, sets or file systems (yes, the FAQ actually mentions swapping some FileSystem objects).

By the way, an instantiation of a "function template" is called "template function".

FQA: In addition to all the problems with class templates, we are now engaged in a battle of wits with the oh-so-smart compiler figuring out template parameters from function arguments. Or not. For example, std::max(x,5) compiles when x is a int, but fails to compile when it's a float or a short. You see, the point of templates is to make "algorithms" work with values of many different types, facilitating "code reuse" (the fact that x>5?x:5 is less code than std::max(x,(short)5) doesn't mean you don't want to reuse code, does it?).

For instance, you can reuse std::swap to swap a couple of file systems. All you have to do is implement a class FileSystem with a default constructor creating a new empty disk partition. The copy constructor will copy all the files from a given FileSystem to a newly created partition, the destructor will wipe it out, and operator= will do the latter followed by the former. To handle errors, use exceptions. You might get a few extra disk partitions created and destroyed, especially if you pass FileSystem objects around too much in the code, but that's a small price to pay for reusing the 3 lines of code in std::swap. And a "commercial-grade" compiler can even eliminate some of those copies!

The note about "function templates" and "template functions" is very useful. Too bad there are people out there that confuse the two. Be careful with C++ terminology. For example, don't confuse "object files" (compiled code) with "objects" (which belong to a class), which in turn shouldn't be confused with "instantiation" of class templates (substituting template parameters is called "instantiation", the result of this process is also "instantiation", and creating objects is called "construction"). The good (or bad) news is that the terminology is the easy part.

[35.4] How do I explicitly select which version of a function template should get called?

FAQ: Most of the time you don't need to do it - the compiler will guess. It uses arguments to guess, so when your function has none, use f<int>();. Sometimes you want to force the compiler to choose a different type than it would choose by default - for example, g(45) will instantiate g<int>, while you want g<long>. You can force the compiler to call g<long> with explicit instantiation as in g<long>(45); or type conversion as in g(45L);.

FQA: There is a good reason to avoid all kinds of "clever" behavior which gets in your way when you try to figure out what a program actually does (it doesn't always do what the author thought it would do). Overloading and template specialization are one kind of this behavior - go figure which function is actually called. But let's assume for a moment that it's not a problem, and that the important thing is to write code expressing the author's intent "clearly" in the sense that it's not cluttered with "low-level" details like which "version" of a function is called.

In that case, the picture presented by the FAQ looks fair - you only need to explicitly specify parameters when the compiler has no information to guess them itself, or when you don't like its guess. The truth is more complicated, because sometimes a function has more than one argument, and the template defines constraints on these arguments. That's what happens with std::max(x,5) when x is a float. The template wants two arguments of the same type, and the compiler thinks that "5" is of type int. So even though the intent seems clear (you probably want 5 to be treated as a number of the same type as that of x), the compiler can't make a decision. So you have to interfere in these cases, and choose between two almost equally unreadable alternatives, explicit instantiation or explicit type conversion.

Out of the two options, explicit instantiation, the syntax defined by C++ (as opposed to type conversion which is inherited from C) is typically worse. First, this way the code using a function forces it to be implemented as a template, although it doesn't really care, and makes it harder to get rid of the pesky templates when you feel like it. And second, when the need to interfere and disambiguate arises inside another template, you may have to use the following syntax, hideous even by C++ standards:

a.template f<long>(45);

While C++ features generally interact poorly with each other, templates set the record by interacting poorly with themselves.

[35.5] What is a "parameterized type"?

FAQ: A way to say "class templates".

FQA: Hmm, why do we need two ways of saying this?

[35.6] What is "genericity"?

FAQ: A way to say "class templates". Don't confuse with "generality".

FQA: Hmm, why do we need three ways of saying this? Apparently C++ promoters consider templates an excellent and unique feature, despite their obscurity and the availability of much better meta-programming facilities in many languages. The many different synonyms are probably needed to illuminate the killer feature from many different directions.

[35.7] My template function does something special when the template type T is int or std::string; how do I write my template so it uses the special code when T is one of those specific types?

FAQ: First, make sure it's a good thing to do in your case. Generally, it is when the "observable behavior" in the special version you want to add is identical to the general case - otherwise, you're not helping your users. If the special case is "consistent" with the generic case, you can do it as in template<> void foo<int>() { ... }.

FQA: "Observable behavior" means different things to different people. In particular, many C++ users tend to observe performance (if you don't care about performance, but you still use a language that won't detect run time violations of language rules like out-of-bounds array indexes, you're probably wasting your time).

Consequently, the specialization of vector<bool> to be space-efficient (by storing bits instead of bytes) at the cost of speed (individual bits are harder to access than whole bytes) found in some STL versions is not a very good idea, because it ultimately confuses the performance-aware user. If the user wants a vector of bits, the user can implement a vector of bits, or STL could supply one, but it's very inconvenient when you can't have a simple mental model describing what vector really means.

In addition, specialization is actually a pretty dangerous trap because it is your responsibility to make sure that all specializations are visible to the compiler (#included) at each point where a template is used. If that's not the case, you can get too kinds of vector<bool> instantiated in your program, triggering "undefined behavior" (typically you'll pass a vector of the first kind to a function compiled to work with vectors of the second kind and crash). So specializing others' templates is very likely to lead to a disaster (because you can't make sure that your specializations are visible in code you didn't write), and libraries which actually assume that you'll specialize templates they define are best avoided.

If you really want to use specialization, take into account that function templates don't support partial specialization ("I want a special version for all types which are vectors of any T"), only class templates do (template functions support overloading, which follows different rules). One workaround is to implement a single function template working with any T and delegate the call to a static method of a template class, as in:

template<class T>
void f(const T& x)
{
  FImpl<T>::f(x);
}

This way, the class FImpl can be defined using partial specialization. All these layers of cryptic syntax could be considered tolerable if they ultimately were the only way to accomplish something really useful, and you could forget about them once you were done. But it's actually very easy to program without these complications, and almost always these complications get you nothing except for reducing maintainability, and you get to see them each time you have a compilation error deep down a chain of templates delegating trivial work to each other, and debugging run time errors becomes a real nightmare.

Basically you can choose simple interfaces and simple implementations, or C++-style cryptic interfaces and cryptic implementations. It's a trade-off.

[35.8] Huh? Can you provide an example of template specialization that doesn't use foo and bar?

FAQ: For instance, you can "stringify" values of different types using a template. The generic version boils down to ostringstream out; out << x. But you might want to define specializations to handle types where the ostream output operator doesn't do what you like (you can set the output precision of floating point numbers, etc.)

FQA: This means that all values of the same type will have to be formatted identically. For example, all integers will be printed using decimal digits. If you prefer hexadecimal, you can define class HexInt (a template, of course, so that it can handle all the different integral types, including user-defined ones). Then you can use stringify(HexInt<int>(x)). You might need a partial specialization of stringify for HexInt<T> (see previous FAQ). To save the trouble of explicitly passing the template parameters to HexInt, use a creator function template HexInt<T> hexint(const T&) - the compiler will figure T out from stringify(hexint(x)). Specifying a number of leading zeros (as in the format string "%08x") using advanced C++ type-based techniques is left as an exercise to the reader.

We've done quite some work indeed in order to print an integer. Time to relax and let the compiler concentrate while it cleverly figures out all the things we want it to figure out. You usually print stuff for debugging, so the long build time may be annoying, but it's sure better than using a visual debugger where you get to see the line noise generated from all those other templates.

In the meanwhile, it is quite likely that whatever your real job was, someone else (probably some kind of competitor) has already done it. But did the result contain a mature, generic and efficient printing infrastructure with really, really minor usability and maintainability problems? Most certainly it didn't. Which is why C++ template specialization is your friend.

[35.9] But most of the code in my template function is the same; is there some way to get the benefits of template specialization without duplicating all that source code?

FAQ: You can factor out the common code and only specialize a helper function called from the common code. Two screens of source code are attached for illustration.

FQA: Factoring out common code and helper functions are indeed very useful (which is why you can do that kind of thing with virtually every programming language). The existence of this question in the FAQ seems to indicate that people get unbelievably confused by templates, and lose hope that anything useful they know is applicable to them. Which is not that much of an exaggeration.

[35.10] All those templates and template specializations must slow down my program, right?

FAQ: You guessed wrong. Maybe the compilation will become "slightly" slower. But the compiler ends up figuring out the types of everything, and then doing all the usual nifty C++ optimizations.

FQA: You guessed right. And the compilation will become intolerably slow. It's not like the FAQ is lying - it's just talking about the state of affairs in theory, where C++ belongs. The "slight" slowdown of compilation is not even worth discussing: everything is "slight" if you have lots of time on your hands. Just try to build a C program and a modern C++ program full of templates and compare the time it took. As to execution time, there are practical problems making programs generated from templates slow compared to the hand-written alternatives.

First, the compiler generates the same code over and over again. Sometimes the linker throws away the extra copies and sometimes it doesn't, and the size of your program increases. In particular, the linker doesn't have a chance to throw away the functions which are identical at the assembly level, but not at the source code level (think about vector<int> and vector<void*>). It is possible to implement templates in a way avoiding these problems (by using the same implementation for all specializations yielding the same assembly code). It is very tedious and almost never done. Two identical functions almost always take more time to execute than a single function called twice, which has to do with instruction caches - a useful gadget frequently overlooked by many people who care about "theoretical efficiency" without actually measuring performance.

Second, when people work with templates, they use types - their only hammer - for saying almost everything (consider the HexInt class from the previous FAQ). More specifically, they wrap simple values of built-in types in user-defined types - classes. The type is used to select the right specialization and what-not - in fact it's used to specify what to do. An extreme example is the boost lambda library - it creates structures representing entire functions, with a sub-structure representing addition, a sub-structure representing the constant 1, etc.

Now, "theoretical performance fans" may think that all of these structures get optimized out by the clever compiler. In practice, that's almost always close to impossible to do because of the so-called pointer aliasing problem. When you have a local variable x, it's clear that nobody can change it but the code of the function, so the compiler can do lots of things with x, like stuffing it into a register or even completely optimizing it out. But once you push x into a structure, it's hard to see where it's modified - go figure who has a pointer to that structure, especially if you pass the object to a separately compiled function. So the compiler has to allocate a memory slot for x and make sure the memory cell gets updated when x is modified and that the memory cell gets read when there's a chance that it could have been changed by someone else. Code working with templates and relying on types to do compile time dispatching ends up doing lots of memory load/store operations at run time since the types don't really go away.

And anyway, there's such a huge amount of scenarios to take care of to optimize complicated template-based code well that compiler writers rarely bother. They are lucky if they get the parsing right. Even that is unlikely - when you are porting from one compiler to another, chances are that most of your compatibility problems will come from the semantics of the code using templates.

[35.11] So templates are overloading, right?

FAQ: They are in the sense that they are inspected when the compiler resolves names (figures out the version of f that should be called by f(x)). They are not in the sense that the rules are different. Specifically, there's the SFINAE (Substitution Failure Is Not An Error) rule: the argument types have to match exactly for a template to be considered in overload resolution. If they don't, the compiler won't try to apply conversions the way it would with a function - instead it will discard the template.

FQA: Aside from the fact that the acronym "SFINAE" interpreted literally doesn't seem to describe what it's supposed to describe, this sounds just a little bit too easy. For example, what does "exact match" mean? Let's have a look at a real life example taken from the GNU implementation of the C++ standard library. I tried to make this short, but there are about 5 distinct stupid things involved, so it was hard. If you get tired in the middle of this and stop, it's probably an indication that you do get the main point - that things are actually very complicated in this department and that these complications are best avoided.

Stupid thing #1: Once upon a time, vector<int>::iterator was a plain old typedef for int* in the GNU STL. Of course this is very, very dangerous: people might use std::vector as if it were just an array of objects - a serious abstraction violation, you could get arrested for that in some jurisdictions. At the beginning of the 21st century, the guys behind GNU STL decided to "fix" this by creating a class template called __normal_iterator. This template serves as a "strict typedef" - it wraps any existing iterator type, such as int*, and delegates all operations to the existing type, but can not be converted to int*. This has many important benefits over the previous implementation, for example, much longer error messages.

Stupid thing #2: As you may know, there are two kinds of iterators defined by STL containers: iterator and const_iterator because of inherent problems with const: const iterator is not at all the same as const_iterator. If STL wanted to be consistent, it would also define volatile_iterator and const_volatile_iterator, and then nobody would even look at STL, which wouldn't necessarily be bad. But they didn't. So now you also need two kinds of "normal iterators" - for int* and const int*.

Stupid thing #3: Of course the GNU STL guys didn't want to define a __normal_const_iterator - after all, making conversion hard for you is an excellent thing, but making it hard for them is a completely different thing. Instead, they decided to support automatic conversion between different instantiations of __normal_iterator, by - of course - delegating the conversion to the wrapped types (templates normally delegate all useful work to someone else; their job is obfuscation). This way, you can compare const and non-const iterators using the same operator, having this elegant prototype:

  template<typename _IteratorL, typename _IteratorR, typename _Container>
  inline bool
  operator>(const __normal_iterator<_IteratorL, _Container>& __lhs,
            const __normal_iterator<_IteratorR, _Container>& __rhs);

Stupid thing #4: STL provides another, seemingly unrelated service to its users. It defines global relational operators which work on arguments of any type. How can they compare objects without knowing anything about them? At this point you can probably guess the answer - of course, they delegate the work to someone else, this time to existing relational operators, using interesting identities such as (a>b) == !(a<=b). This way, you can define only 2 relational operators, and get the rest "for free".

Stupid thing #5: Except when you can't. This is where our subject, overload resolution and templates, kicks in. Remember the rule about only considering templates when the argument types match the prototype exactly? Well, when you compare "normal iterators" of the same type (for example, both wrapping int*), the beautiful prototype above matches the arguments "exactly". So do the "generic" relational operators. Oops, we have ambiguous overloading! The "solution" is to define a third overload, matching the types even more exactly by using a single template parameter _Iterator instead of two which can possibly differ. Why is this situation considered "unambiguous"? Frankly, beyond the basic intuition saying that you must show the compiler a type pattern as similar to your arguments as possible, I don't know. That's why I didn't list the Stupid thing #6. But the first 5 make me feel that in this case, ignorance is bliss.

Apparently this situation looks discouraging even from inside the C++ universe, as indicated by the following rather sad comment found in one of the header files of the GNU STL. You can probably decipher it, unless the 5 stupid things above have already faded from your memory:

  // Note: In what follows, the left- and right-hand-side iterators are
  // allowed to vary in types (conceptually in cv-qualification) so that
  // comparison between cv-qualified and non-cv-qualified iterators be
  // valid.  However, the greedy and unfriendly operators in std::rel_ops
  // will make overload resolution ambiguous (when in scope) if we don't
  // provide overloads whose operands are of the same type.  Can someone
  // remind me what generic programming is about? -- Gaby

This could be amusing (an implementor of the standard library of a language complaining about this language in files delivered to users and all) if it weren't so mind-numbing. People who think they are better at C++ than the GNU STL authors are welcome to waste their entire life chasing and "solving" problems with overload resolution, template specialization or whatever its name is. For the rest, trying to avoid templates & overloading sounds like a good advice, which can be followed to an extent even if you are forced to use C++.

[35.12] Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?

FAQ: "Accept these facts", says the FAQ - templates are not code, just a recipe for generating code given parameters; in order to compile this code, it must first be generated, which takes knowing both the template definition and the definitions of the parameters which are passed to it; and the compiler doesn't know anything about code outside of a file when compiling the file (which is called "separate compilation").

So you have to place template definitions in header files, or else the compiler won't get a chance to see all the definitions it needs at the same time. Experts should calm down - yes, it's oversimplified; if you know it is, you don't need this answer anyway.

FQA: There are two problems with placing template definitions in header files which may bother you: you get to recompile them each time the file is included, and you disclose your source code to the user. Let's concentrate on the first problem. The second is minor anyway because source code of templates isn't necessarily easier to understand than disassembled object code. As to the first problem - to get an idea about its magnitude, consider the fact that an iostream-based "hello, world" program requires the GNU C++ compiler to parse 718K (!!) bytes. And contrary to the claims in the FAQ, it turns out that the need to make the source code of templates available to the compiler is not the only reason we have this problem.

Suppose a C++ compiler could use some rules to locate compiled definitions of classes given their names, for example "std::vector<int> is always located at the file $OUTPUT_ROOT/templates/std/vector_int.o" and so on. This way, if you used vector<int> and vector<double>, you'd have to compile vector twice, but if you used vector<int> twice, the compiler could avoid recompilation. That would make sense since you'd only compile different classes each time you compile the template.

Unfortunately, this can't work in C++. That's because the compiler can't parse std::vector<int> without parsing the entire preprocessed output generated by #include <vector>. That parsing, which has to be done over and over again for each compiled source file, takes most of the compilation time. Generating the code of std::vector<int> several times is the small part of the problem, and most compiler writers don't bother to solve it, since you'd still have the parsing bottleneck.

The basic problem inherited from C is that the compiler can't look up definitions. Instead, you have to arrange #include files so that the preprocessor can copy-and-paste definitions into a single huge bulk containing everything relevant (as well as many irrelevant things) for the compilation of your source file. C still compiles fast because its grammar is simple. Many newer languages define not only the concept of a "class", but also rules to help the compiler locate definitions instead of parsing them over and over again. C++ programmers enjoy the worst of both worlds.

[35.13] How can I avoid linker errors with my template functions?

FAQ: You probably didn't make the definition of a template available to your compiler at the point where a template is used (did you implement a template in a .cpp file?). There are three solutions:

FQA: These "solutions" create new problems:

[35.14] How does the C++ keyword export help with template linker errors?

FAQ: It's "designed" to eliminate the need to make the definition of a template available to the compiler at the point of usage. Currently, there's only one compiler supporting it. The keyword's future is "unknown".

An advice for futuristic programmers follows. It shows a way to make code compatible with both compilers that support export and those that don't using - guess what? - the wonders of the evil C preprocessor. Among other things, the FAQ advises to #define export under certain conditions.

FQA: "How does it help", is that what you want to know? OK then. The export keyword helps with template linker errors just the way a song about peace helps to stop a bullet penetrating a foot. It helps just like a keyword findbugs telling the compiler to find and report all the bugs in a piece of code would help you with bugs.

The rest of C++ makes this keyword impossible to support in any useful way that would actually yield faster compilation compared to the case when template definitions are included at header files. That's why most compilers don't bother to support it, and that's why the future of the keyword is "unknown": it's useless.

If you spot someone following the FAQ's advice (#ifdef EXTRATERRESTRIAL_COMPILER and all that), call an ambulance. Warning: the patient has likely reached a very agitated state and might escape before the people qualified to deal with the situation arrive. Try to occupy the patient's mind with a discussion about the fact that #defining keywords is illegal C++. Propose to consult your lawyer. Try to "design" a couple of keywords together (look up synonyms in a dictionary, imagine them printed in popular fonts, stuff like that). Improvise. It's gonna be over soon.

[35.15] How can I avoid linker errors with my template classes?

FAQ: It's just like errors with template functions, which were explained in the previous answers.

FQA: Yep, it's about the same.

[35.16] Why do I get linker errors when I use template friends?

FAQ: If you have a class template C declaring a friend like Foo<T> f(), the compiler assumes that there's a global function f() returning Foo<T>. You probably meant a different thing, namely - there's a function template template<class T> Foo<T> f(), and you want its instantiation f<T>() to be a friend of your instantiation C<T>.

There are two ways around this:

FQA: In both solutions the syntax is unrelated to the semantics to an extent remarkable even for C++.

Why does <> mean that we are talking about an instantiation of a template function? Why not use a keyword (like, just an example off the top of the head, the template keyword) to say that?! The C++ way is uncompromisingly ugly, especially in the example mentioned by the FAQ itself: operator<< <>(...).

And even when you don't have a problem with placing the definition in a header file, the seemingly cleaner second way is actually more cryptic. This breaks one of the very few things in C++ you can normally count on: that a declaration of a function or a type looks just like a definition without the body. Here, we change the meaning of the prototype by adding a body: instead of declaring a function, we now declared and defined a function template.

Last but not least, the very fact that this problem exists is an indication of a readability problem with the C++ grammar. How many people would guess that the original declaration refers to a function and not a function template?

How is one supposed to navigate through this swamp of arbitrary syntax? Of course one shouldn't expect the kind of readability you get with a natural language from a programming language. Of course any formal language will behave "counter-intuitively" at times. But people do deal with formal languages quite successfully, when it is possible to keep a reasonably compact model of the key rules in one's mind. In these cases, even if you bump into a behavior which doesn't make sense at the first glance, you can think again and - "Of course, of course, I know what it's doing!". Do you feel that you understand what a C++ compiler is actually doing? Neither do most C++ users out there.

[35.17] How can any human hope to understand these overly verbose template-based error messages?

FAQ: There's a "free tool" converting compiler error messages to more human-readable ones. It works with many compilers.

An example follows, having a snippet [STL Decryptor: Suppressed 1 more STL standard header message] in it.

FQA: Oh really? Any "template-based" error messages? Hmm, why didn't the compiler writers produce clean error messages in the first place if a single tool can clean up all the mess created by many different compilers? These people must be quite lazy and/or stupid. Or are they?

Actually, no, they are not. The error messages are cryptic because templates are cryptic, and most compilers can't really do much better than they do today.

The tool mentioned (without the name) and linked to by the FAQ is called STLFilt. That's why the FAQ doesn't mention the name. That's why its output does mention STL. That's why it works at all - you can't improve generic template error messages, but you can filter STL-related messages if you know how STL is implemented in each specific case.

We need a couple more tools, like STLCompilationTimeReducer and STLDebugInformationBeautifier, and we're all set. Yet another proof that generic meta-programming facilities are a better way to implement containers than build them into a language.

If you wish to implement a template library, don't forget to implement a tool filtering the error messages your users will get, as well as the other cool tools, for all the flavors of compilers out there.

[35.18] Why am I getting errors when my template-derived-class uses a nested type it inherits from its template-base-class?

FAQ: This can hurt, sit down. The compiler doesn't look for "non-dependent" names (ones that don't mention the template parameters) in "dependent" base classes. So if you inherited a nested class or typedef A from your base class B<T>, you can only access it using a dependent name, like B<T>::A, but you can't use a non-dependent name, like plain A.

And you'll have to prefix that "dependent" name with the typename keyword. That's because the compiler doesn't know that B<T>::A is a type (think about two specializations, one defining a nested class A and one defining a global variable A).

FQA: This illustrates two generic problems with the C++ grammar.

First, class templates are not just parameterized class definitions, because the crazy C++ name look-up gets crazier when you are inside a template. So don't assume you can take a C++ class definition, factor out a bunch of parameters and get a working template definition.

Second, telling a C++ type name from a C++ object/function name is insanely complicated. This interacts badly with templates, constructors, and everything else.

As to the possible reactions of users the FAQ attempts to anticipate: while running away may be justified, sitting down is probably not. Good developers tend to test their code, so even if the compiler didn't spit an error message and did the wrong thing silently (for instance, used a type called A from the global namespace), a test will find the error. Stupid compiler behavior only feels like pain for people who think that the extremely slow C++ compilers spend their time in finding all their bugs, and don't bother to test the result. Those people should relax and save their tears for the C++ run-time errors.

[35.19] Why am I getting errors when my template-derived-class uses a member it inherits from its template-base-class?

FAQ: The reasons are identical to those in the previous FAQ. But the workarounds are different - convert f() to this->f() or add the statement using B<T>::f() to your class definition. Using the fully qualified name like it was done in the previous FAQ (B<T>::f()) will also work, except when the function is virtual, in which case the compiler will use static binding, not dynamic binding.

FQA: Yep, the case with member functions is similar to the case with nested types, with the additional bonus of interacting badly with virtual functions.

The FAQ has a hilarious comment in the spirit of "this doesn't mean that template inheritance doesn't work - but the name look-up works differently". And that's C++ for you: it's not like you can't write code, it's just that you can't tell for sure what any particular name or some other part of it means.

[35.20] Can the previous problem hurt me silently? Is it possible that the compiler will silently generate the wrong code?

FAQ: Yes - the compiler might call a function or use a type from the global namespace instead of what you meant, for example.

FQA: It's not as horrible that a language can silently misinterpret what you mean as it may sound. Any language will do this to the creative but imprecise human mind (formal languages aside, people frequently misunderstand each other).

With formal languages, you can form a relatively simple model which will help you understand these problems. It's the same with C++ except for the "simple" part, so C++ and you will misunderstand each other pretty frequently.

And you can also test your code by creating programs that check if it does what you want it to do in a bunch of cases. It's the same with C++ except that it compiles forever and you have to write notable amounts of code to implement the simplest test, so C++ code ends up being tested pretty rarely.


Copyright © 2007-2009 Yossi Kreinin
revised 17 October 2009