I've used templates in my C++ code, but never in a very "hard-core" way. I don't think I ever used partial specialization or template members, for instance. Definitely not metaprogramming. I guess these techniques just aren't in my 20% subset of C++, and I feel good about it, since they're not in most people's 20% subset of C++. However, I do sometimes run into complex template constructs in code I want to understand, and not even grokking the syntax of what I'm seeing is kinda humiliating.
So this article will fix this. It's a collection of "syntax patterns" for templates, describing in brief what each one is, leaving the more complete explanation to Google searches. The idea is when I run into another unfamiliar piece of C++ template code in the future, I'll look here and understand what I'm seeing.
This is not one of my usual articles - I don't intend to teach anything here. It is a keeper of information for my future self, and I plan to expand it from time to time. I did decide to make it public in the hope that it will help other people, and also to face public scrutiny - so if you have corrections or additions, please let me know.
Class templates and function templates
For the sake of completeness, the basics of templates:
template <typename T>
class Array {
... // blah blah
int len() const;
};
This is a class template. Here's how its method definition can look:
template <typename T>
int Array<T>::len() const
{
...
}
When instantiated, the template parameter must be made explicit:
Array<int> int_arr;
Array<Person*> people;
The following is a function template:
template<typename T>
void swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
To use it, the template parameter can be omitted because the compiler can infer it from the types of the arguments:
int aa, bb;
// stuff assigning aa and bb
swap(aa, bb); // calls swap<int>(...);
And you can also set it explicitly, if you're so inclined [1]:
int aa, bb;
// stuff assigning aa and bb
swap<int>(aa, bb);
Inferring only part of the parameters is possible as well:
template <class X, class Y>
bool same_size_as_template(const Y& val)
{
return sizeof(X) == sizeof(Y);
}
Y can be inferred from the type of the argument passed to the function, so we can use it as follows:
cerr << same_size_as_template<double>(5) << endl;
Non-type template parameters
Template parameters are usually types (typename foo, class foo etc), but they don't have to be. Template parameters can also be constant integral values (including enumerations), as well as some other more esoteric things I'll ignore at this point. It looks like this:
template <typename T, int N>
class Foo {
};
N can then be used like any other constant in the code of Foo. The most interesting use-case is probably using it as an array size (which must be constant).
Default values for template parameters
Template parameters may have default values, and when instantiating the template these values can be omitted. Here's an example:
template <typename T=int, int N=10>
class Foo {
};
Foo<float, 42> foo1;
Foo<double> foo2;
Foo<> foo3;
Note specifically the strange syntax for the definition of foo3, which means that it instantiates the Foo template with all parameters assigned their default values.
Default values may only be specified for class templates.
Specialization
Here is a generic Array container, plus its specialization for bool:
template <typename T>
class Array {
... // Generic Array code
};
template <>
class Array<bool> {
... // Special code for Array<bool>
};
For function templates, there's another way to specialize, using the fact that the compiler can deduce argument types from the function's argument list:
template <typename T> bool less(T aa, T bb)
{
return aa < bb;
}
// Specialize for T = const char*
// Could be also specified explicitly in <..> after less,
// but it isn't necessary
//
template<> bool less(const char* aa, const char* bb)
{
return strcmp(aa, bb) < 0;
}
Partial specialization
Partial specialization seems to refer to two slightly different syntaxes. One is specializing a generic class (or function) for some modifier of a type, for example:
template <typename T>
class Array<T*> {
... // Specialized for array of pointers to any type
};
Is a partial specialization of the aforementioned generic Array for pointer types.
Another face of partial specialization is taking a template with more than one parameter and specializing it by some of the parameters. A good example is the actual std::vector container which is defined roughly as follows:
template<typename T, typename Allocator>
class vector {
... // vector contents
};
And here is its partial specialization for bool:
template<typename Allocator>
class vector<bool, Allocator> {
... // vector<bool> contents
};
Member templates
Class members (both member functions and nested classes) can also be templated. Consider this, for example:
template <typename T>
class Array {
... // blah blah
int len() const;
template <typename V>
Array<T>& operator=(const Array<V>& other) {
...
}
};
Normally, you can't assign a Array<int> to Array<double>, even though int is convertible to double. With the template member above, you can. Just implement the operator= appropriately, and assigning one Array to another will be possible as long as the type conversion between the contained types is supported.
Disambiguating dependent qualified type names
Suppose you have a function template in which you want to instantiate a vector iterator. Something like the following:
template <class T>
string foo(vector<T> vec, ... other args)
{
vector<T>::iterator it = vec.begin();
}
Unfortunately, this is invalid C++ and the compiler will complain. The problem is that vector<T>::iterator is a qualified and dependent name, and the compiler can't be sure whether it refers to a type or a member before it sees T.
I won't spend too much time explaining the exact mechanics, they are easily discoverable by some googling (one good resource is this article). I just want to mention that to resolve this ambiguity for the compiler, the typename keyword must be used as follows:
template <class T>
string foo(vector<T> vec, ... other args)
{
typename vector<T>::iterator it = vec.begin();
}
Disambiguating explicitly qualified template member usage
Take:
class Foo
{
public:
template<class T> T member_func();
};
member_func is a member template. Suppose we want to use it with an explicit type qualification:
template<class U> void func(U arg)
{
int obj = arg.member_func<int>();
}
This is invalid since the compiler can't parse arg.member_func<int>() correctly. It thinks the first < is a less-than sign, and arg.member_func refers to some non-templated member of arg (arg may very well have such a member, but since it's a templated type the compiler doesn't know for sure until it's instantiated).
To disambiguate, the template keywords has to be explicitly used thus:
template<class U> void func(U arg)
{
int obj = arg.template member_func<int>();
}
Note that this rule applies also to both -> and :: between the object and member names.
Resources
- C++ FAQ Lite, especially chapter 35
- "The C++ Programming Language, 3rd edition" by Bjarne Stroustrup
- "C++ Templates: The Complete Guide" by David Vandevoorde and Nicolai M. Josuttis
- "A description of the C++ typename keyword"
- "Tech talk about C++ templates"
[1] | You'd want to use this when the compiler can't infer the template parameter - for example for functions that accept no arguments but should still be templated. |