Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
A non-type template parameter must be an integral value that
is known at compile time. You can make a fixed-size Stack, for instance,
by specifying a non-type parameter to be used as the dimension for the
underlying array, as follows.
template<class T, size_t N> class Stack {
T data[N]; // Fixed capacity is N
size_t count;
public:
void push(const T& t);
// Etc.
};
You must provide a compile-time constant value for the
parameter N when you request an instance of this template, such as
Stack<int, 100> myFixedStack;
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. This 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<int, 100>. This
means that each distinct value of N results in a unique class type. For
example, Stack<int, 99> is a distinct class from Stack<int, 100>.
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 specifies 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 <bitset>
#include <cstddef>
#include <cstdlib>
#include <ctime>
using std::size_t;
using std::bitset;
template<size_t UpperBound> class Urand {
bitset<UpperBound> used;
public:
Urand() { srand(time(0)); } // Randomize
size_t operator()(); // The "generator"
function
};
template<size_t UpperBound>
inline size_t Urand<UpperBound>::operator()() {
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 numbers generated by Urand are unique because the
bitset used tracks all the possible numbers in the random space
(the upper bound is set with the template argument) and records each used number
by setting the corresponding position bit. 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 <iostream>
#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.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |