Template Parameters and Arguments

A template can take parameters:
Type parameters of “type type”.
Value parameters of built-in types such as ints and pointers to functions.
Template parameters of “type template”.

Types as Arguments

A type argument is unconstrained. You can implement general constraints as concepts.

Values as Arguments

Example:

1
2
3
4
5
6
7
8
9
10
template <typename T, int max>
class Buffer {
T v[max];
public:
Buffer();
// ...
};

Buffer<char, 128> cbuf;
Buffer<int, 5000> ibuf;

The argument for a template value can only be:
• An integral constant expression.
• A pointer or a reference to an object or function with external linkage.
• A nonoverload pointer to a member.
• A null pointer.

The restriction exists to simplify implementation of separately compiled translation units. For doubles and other arguments, we can always find out a better and more clever method to implement.

A type template parameter can be used as a type later in a template parameter list and it becomes useful when combined with a default template arguments:

1
2
3
4
5
6
7
template <typename T, T default_value = T{}>
class Vec {
// ...
};

Vec<int, 453> c1;
Vec<int> c11; // default_value is int{}, that is 0.

Operations as Arguments

For the standard-library map, for a simplified version:

1
2
3
4
template <typename Key, class V>
class map {
// ...
};

How do we supply comparison criteria for keys?
Because we can’t hardwrite the criterion, so the notion of soring criterion for a map could be represented as:
• A template value argument (e.g., a pointer to a comparison function).
• A template type argument to the map template determining the type of a comparison object.

A template value argument

It looks like this:

1
2
3
4
5
template<typename Key, typename V, bool(*cmp)(const Key&, const Key&)>
class map {
public:
map() = default;
}

And at this time, we need user to provide us with a comparative function:

1
2
3
4
bool insensitive(const string& x, const string& y) {
// compare
}
map<string, int, insensitive> m;

However, it’s not flexible:
• The designer of map will have to decide whether to compare the Key type using a pointer to function or a function object of some specific type.
• Because the argument types of the comparison operator must depend on the Key type, it can be hard to provide a default comparison criterion.

A template type argument

This method is used in the standard library:

1
2
3
4
5
6
7
8
template<typename Key, typename V, typename Compare = std::less<Key>>
class map {
public:
map() = default;
map(Compare c) : cmp(c) {}
//...
Compare cmp{}; //default comparison
};

So we can call map like below:

1
2
map<string, int> m1; // use default comparison
map<string, int, std::greater<string>> m2;

And function objects can carry states:

1
2
Complex_compare f3{"French", 3}; // make a comparison object
map<string, int, Complex_compare> m2{f3}; //compare using f3;

Also, we can use lambda:
example 1:

1
2
using Cmp = bool(*)(const string&, const string&);
map<string, int, Cmp> m3{ [](const string& a, const string& b) {return a > b;} };

example 2:

1
2
auto cmp = [](const string& x, const string& y) const { return x < y; }
map<string, int, decltype(cmp)> m4{cmp};

Templates as Arguments

At some times, we want to use one template as another template’s argument:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T, template<typename> class C>
class Xref {
C<T> mems;
C<T*> res;
};

template<typename T>
using My_vec = vector<T>;

Xref<int, My_vec> ref1;

template<typename T>
class My_container {
//...
};

Xref<Record, My_container> ref2;

Only class templates can be template arguments.

Another common case in which a template needs only a container or two is often better handled by passing the container types:

1
2
3
4
5
6
7
template<typename C1, typename C2>
class Xref2 {
C1 mems;
C2 refs;
};

Xref2<vector<Entry>, vector<int>> x;

Default Template Arguments

Like default function arguments, the default template arguments can be specified and supplied for trailing arguments only:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void f1(int x = 0, int y);  // error: default argument not trailing
void f2(int x = 0, int y = 1); // ok

f2(, 2); //syntax error
f2(2); // call f(2, 1);

template<typename T1 = int, typename T2>
class X1 { // error: default argument not trailing.
};

template<typename T1 = int, typename T2 = double>
class X2 { // ok
};

X2<, float> v1; //syntax error
X2<float> v2; // v2 is an X2<float, double>

Default Function Template Arguments

Naturally, default template arguments can also be useful for function templates.