Expression arguments have some restrictions. An expression argument can be an integer type, an enumeration type, a reference, or a pointer. Thus, double m is ruled out, but double & rm and double * pm are allowed. Also the template code can’t alter the value of the argument or take its address. Thus, in the ArrayTP template, expressions such as n++ or &n would not be allowed. Also when you instantiate a template, the value used for the expression argument should be a constant expression.
This approach for sizing an array has one advantage over the constructor approach used in Stack. The constructor approach uses heap memory managed by new and delete, whereas the expression argument approach uses the memory stack maintained for automatic variables. This provides faster execution time, particularly if you have a lot of small arrays.
The main drawback to the expression argument approach is that each array size generates its own template. That is, the following declarations generate two separate class declarations:
ArrayTP
ArrayTP
But the following declarations generate just one class declaration, and the size information is passed to the constructor for that class:
Stack
Stack
Another difference is that the constructor approach is more versatile because the array size is stored as a class member rather than being hard-coded into the definition. This makes it possible, for example, to define assignment from an array of one size to an array of another size or to build a class that allows resizable arrays.
Template Versatility
You can apply the same techniques to template classes as you do to regular classes. Template classes can serve as base classes, and they can be component classes. They can themselves be type arguments to other templates. For example, you can implement a stack template by using an array template. Or you can have an array template that is used to construct an array whose elements are stacks based on a stack template. That is, you can have code along the following lines:
template
class Array
{
private:
T entry;
...
};
template
class GrowArray : public Array
template
class Stack
{
Array
...
};
...
Array < Stack
In the last statement, C++98 required separating the two > symbols by at least one white space character in order to avoid confusion with the >> operator. C++11 removes this requirement.
Using a Template Recursively
Another example of template versatility is that you can use templates recursively. For example, given the earlier definition of an array template, you can use it as follows:
ArrayTP< ArrayTP
This makes twodee an array of 10 elements, each of which is an array of five ints. The equivalent ordinary array would have this declaration:
int twodee[10][5];
Note that the syntax for templates presents the dimensions in the opposite order from that of the equivalent ordinary two-dimensional array. The program in Listing 14.18 tries this idea. It also uses the ArrayTP template to create one-dimensional arrays to hold the sum and average value of each of the 10 sets of five numbers. The method call cout.width(2) causes the next item to be displayed to use a field width of two characters, unless a larger width is needed to show the whole number.
Listing 14.18. twod.cpp
// twod.cpp -- making a 2-d array
#include
#include "arraytp.h"
int main(void)
{
using std::cout;
using std::endl;
ArrayTP
ArrayTP
ArrayTP< ArrayTP
int i, j;
for (i = 0; i < 10; i++)
{
sums[i] = 0;
for (j = 0; j < 5; j++)
{
twodee[i][j] = (i + 1) * (j + 1);
sums[i] += twodee[i][j];
}
aves[i] = (double) sums[i] / 10;
}
for (i = 0; i < 10; i++)
{
for (j = 0; j < 5; j++)
{
cout.width(2);
cout << twodee[i][j] << ' ';
}
cout << ": sum = ";
cout.width(3);
cout << sums[i] << ", average = " << aves[i] << endl;
}
cout << "Done.\n";
return 0;
}
The output of the program in Listing 14.18 has one line for each of the 10 elements of twodee, each of which is a five-element array. Each line shows the values, sum, and average of an element of twodee:
1 2 3 4 5 : sum = 15, average = 1.5
2 4 6 8 10 : sum = 30, average = 3
3 6 9 12 15 : sum = 45, average = 4.5
4 8 12 16 20 : sum = 60, average = 6
5 10 15 20 25 : sum = 75, average = 7.5
6 12 18 24 30 : sum = 90, average = 9
7 14 21 28 35 : sum = 105, average = 10.5
8 16 24 32 40 : sum = 120, average = 12
9 18 27 36 45 : sum = 135, average = 13.5
10 20 30 40 50 : sum = 150, average = 15
Done.
Using More Than One Type Parameter