Because the value of N is known at compile time, the underlying array (data) can be placed on the runtime stack instead of on the free store, which can improve runtime performance by avoiding the overhead associated with dynamic memory allocation. Following the pattern mentioned earlier, the name of the class above is Stack
The bitset class template, discussed in detail in Chapter 7, is the only class in the standard C++ library that uses a non-type template parameter, which happens to specify the number of bits the bitset object can hold. The following random number generator example uses a bitset to track numbers so all the numbers in its range are returned in random order without repetition before starting over. This example also overloads operator( ) to produce a familiar function-call syntax.
//: C05:Urand.h
//{-bor}
// Unique randomizer
#ifndef URAND_H
#define URAND_H
#include
#include
#include
#include
using std::size_t;
using std::bitset;
template
class Urand {
bitset
public:
Urand() {
srand(time(0)); // randomize
}
size_t operator()(); // The "generator" function
};
template
inline size_t Urand
if(used.count() == UpperBound)
used.reset(); // start over (clear bitset)
size_t newval;
while(used[newval = rand() % UpperBound])
; // Until unique value is found
used[newval] = true;
return newval;
}
#endif // URAND_H ///:~
The uniqueness of Urand is produced by tracking with a bitset all the numbers possible in the random space (the upper bound is set with the template argument) and by recording each number as it’s used by setting the corresponding position bit in used. When the numbers are all used up, the bitset is cleared to start over. Here’s a simple driver that illustrates how to use a Urand object:.
//: C05:UrandTest.cpp
//{-bor}
#include
#include "Urand.h"
using namespace std;
int main() {
Urand<10> u;
for(int i = 0; i < 20; ++i)
cout << u() << ' ';
} ///:~
As we explain later in this chapter, non-type template arguments are also important in the optimization of numeric computations.
Default template arguments
You can provide default arguments for template parameters in class templates. (They are not allowed in function templates.) As with default function arguments, they should only be defined once, the first time a template declaration or definition is seen by the compiler; and once you introduce a default argument, all the subsequent template parameters must also have defaults. To make the fixed-size Stack template shown earlier a little friendlier, for example, you can add a default argument like this:.
template
class Stack {
T data[N]; // Fixed capacity is N
size_t count;
public:
void push(const T& t);
// etc.
};
Now, if you omit the second template argument when declaring a Stack object, the value for N will default to 100.
You can choose to provide defaults for all arguments, but you must use an empty set of brackets when declaring an instance so that the compiler knows that a class template is involved. Here’s how:
template
class Stack {
T data[N]; // Fixed capacity is N
size_t count;
public:
void push(const T& t);
// etc.
};
Stack<> myStack; // Same as Stack
Default arguments are used heavily in the standard C++ library. The vector class template, for instance, is declared as follows:
template
class vector;
Note the space between the last two right angle bracket characters. This prevents the compiler from interpreting those two characters (>>) as the right-shift operator.
This declaration reveals that vector actually takes two arguments: the type of the contained objects it holds, and a type that represents the allocator used by the vector. (We talk more about allocators in Chapter 7.) Whenever you omit the second argument, the standard allocator template is used, parameterized by the first template parameter. This declaration also shows that you can use template parameters in other, subsequent template parameters, as T is used here.
Although you cannot use default template arguments in function templates, you can use template parameters as default arguments to normal functions. The following function template adds the elements in a sequence.
//: C05:FuncDef.cpp
#include
using namespace std;
template
T sum(T* b, T* e, T init = T()) {
while(b != e)
init += *b++;
return init;
}
int main() {
int a[] = {1,2,3};
cout << sum(a, a+sizeof a / sizeof a[0]) << endl; // 6
} ///:~
The third argument to sum( ) is the initial value for the accumulation of the elements. Since we omitted it, this argument defaults to T( ), which in the case of int and other built-in types invokes a pseudo-constructor that performs zero-initialization.
Template template parameters