The third type of parameter a template can accept is another class template. This may sound strange, since templates are types, and type parameters are already allowed, but if you are going to use a template type parameter as a template in your code, the compiler needs to know that the parameter is a template in the first place. The following example illustrates a template template parameter.
//: C05:TempTemp.cpp
// Illustrates a template template parameter
#include
#include
using namespace std;
template
class Array { // A simple, expandable sequence
enum {INIT = 10};
T *data;
size_t capacity;
size_t count;
public:
Array() {
count = 0;
data = new T[capacity = INIT];
}
void push_back(const T& t) {
if(count == capacity) {
// Grow underlying array
size_t newCap = 2*capacity;
T* newData = new T[newCap];
for (size_t i = 0; i < count; ++i)
newData[i] = data[i];
delete data;
data = newData;
capacity = newCap;
}
data[count++] = t;
}
void pop_back() {
if(count > 0)
--count;
}
T* begin() {
return data;
}
T* end() {
return data + count;
}
};
template
class Container {
Seq
public:
void append(const T& t) {
seq.push_back(t);
}
T* begin() {
return seq.begin();
}
T* end() {
return seq.end();
}
};
int main() {
Container
theData.append(1);
theData.append(2);
int* p = theData.begin();
while(p != theData.end())
cout << *p++ << endl;
} ///:~
The Array class template is a trivial sequence container. The Container template takes two parameters: the type of the objects it is to hold, and a sequence data structure to do the holding. The following line in the implementation of the Container class requires that we inform the compiler that Seq is a template:.
Seq
If we hadn’t declared Seq to be a template template parameter, the compiler would complain here that Seq is not a template, since we’re using it as such. In main( ) a Container is instantiated to use an Array to hold integers, so Seq stands for Array in this example.
Note that it is not necessary in this case to name the parameter for Seq inside Container’s declaration. The line in question is:
template
Although we could have written
template
the parameter U is not needed anywhere. All that matters is that Seq is a class template that takes a single type parameter. This is analogous to omitting the names of function parameters when they’re not needed, such as when you overload the post-increment operator:.
T operator++(int);
The int here is merely a placeholder and therefore needs no name.
The following program uses a fixed-size array, which has an extra template parameter representing the array dimension:
//: C05:TempTemp2.cpp
// A multi-variate template template parameter
#include
#include
using namespace std;
template
class Array {
T data[N];
size_t count;
public:
Array() { count = 0; }
void push_back(const T& t) {
if(count < N)
data[count++] = t;
}
void pop_back() {
if(count > 0)
--count;
}
T* begin() { return data; }
T* end() { return data + count; }
};
template
class Container {
Seq
public:
void append(const T& t) { seq.push_back(t); }
T* begin() { return seq.begin(); }
T* end() { return seq.end(); }
};
int main() {
const size_t N = 10;
Container
theData.append(1);
theData.append(2);
int* p = theData.begin();
while(p != theData.end())
cout << *p++ << endl;
} ///:~
Once again, parameter names are not needed in the declaration of Seq inside Container’s declaration, but we need two parameters to declare the data member seq, hence the appearance of the non-type parameter N at the top level.
Combining default arguments with template template parameters is slightly more problematic. The When the compiler looks at the inner parameters of a template template parameter, default arguments are not considered, so you have to repeat the defaults in order to get an exact match. The following example uses a default argument for the fixed-size Array template and shows how to accommodate this quirk in the language.
//: C05:TempTemp3.cpp
//{-bor}
//{-msc}
// Combining template template parameters and
// default arguments
#include
#include
using namespace std;
template
class Array {
T data[N];
size_t count;
public:
Array() { count = 0; }
void push_back(const T& t) {
if(count < N)
data[count++] = t;
}
void pop_back() {
if(count > 0)
--count;
}
T* begin() { return data; }
T* end() { return data + count; }
};
template
class Container {
Seq
public:
void append(const T& t) { seq.push_back(t); }
T* begin() { return seq.begin(); }
T* end() { return seq.end(); }
};
int main() {
Container
theData.append(1);
theData.append(2);
int* p = theData.begin();
while(p != theData.end())
cout << *p++ << endl;
} ///:~
It is necessary to include the default dimension of 10 in the line:
template