zy.b(); // Doesn't create Z
} ///:~
Here, even though the template Z purports to use both f( ) and g( ) member functions of T, the fact that the program compiles shows you that it only generates Z
Suppose you have a container, a Stack say, and you use specializations for int, int*, and char*. Three versions of Stack code will be generated and linked as part of your program. One of the reasons for using a template in the first place is so you don’t have to replicate code by hand; but code still gets replicated—it’s just the compiler that does it instead of you. You can factor the bulk of the implementation for storing pointer types into a single class by using a combination of full and partial specialization. The key is to fully specialize for void* and then derive all other pointer types from the void* implementation so the common code can be shared. The program below illustrates this technique.
//: C05:Nobloat.h
// Shares code for storing pointers in a Stack
#ifndef NOBLOAT_H
#define NOBLOAT_H
#include
#include
#include
// The primary template
template
class Stack {
T* data;
std::size_t count;
std::size_t capacity;
enum {INIT = 5};
public:
Stack() {
count = 0;
capacity = INIT;
data = new T[INIT];
}
void push(const T& t) {
if (count == capacity) {
// Grow array store
std::size_t newCapacity = 2*capacity;
T* newData = new T[newCapacity];
for (size_t i = 0; i < count; ++i)
newData[i] = data[i];
delete [] data;
data = newData;
capacity = newCapacity;
}
assert(count < capacity);
data[count++] = t;
}
void pop() {
assert(count > 0);
--count;
}
T top() const {
assert(count > 0);
return data[count-1];
}
size_t size() const {return count;}
};
// Full specialization for void*
template<>
class Stack
void** data;
std::size_t count;
std::size_t capacity;
enum {INIT = 5};
public:
Stack() {
count = 0;
capacity = INIT;
data = new void*[INIT];
}
void push(void* const & t) {
if (count == capacity) {
std::size_t newCapacity = 2*capacity;
void** newData = new void*[newCapacity];
std::memcpy(newData, data, count*sizeof(void*));
delete [] data;
data = newData;
capacity = newCapacity;
}
assert(count < capacity);
data[count++] = t;
}
void pop() {
assert(count > 0);
--count;
}
void* top() const {
assert(count > 0);
return data[count-1];
}
std::size_t size() const {return count;}
};
// Partial specialization for other pointer types
template
class Stack
typedef Stack
public:
void push(T* const & t) {Base::push(t);}
void pop() {Base::pop();}
T* top() const {return static_cast
std::size_t size() {return Base::size();}
};
#endif // NOBLOAT_H ///:~.
This simple stack expands as it fills its capacity. The void* specialization stands out as a full specialization by virtue of the template<> prefix (that is, the template parameter list is empty). As mentioned earlier, it is necessary to implement all member functions in a class template specialization. The savings occurs with all other pointer types. The partial specialization for other pointer types derives from Stack
//: C05:NobloatTest.cpp
#include
#include
#include "Nobloat.h"
using namespace std;
template
void emptyTheStack(StackType& stk) {
while (stk.size() > 0) {
cout << stk.top() << endl;
stk.pop();
}
}
// An overload for emptyTheStack (not a specialization!)
template
void emptyTheStack(Stack
while (stk.size() > 0) {
cout << *stk.top() << endl;
stk.pop();
}
}
int main() {
Stack
s1.push(1);
s1.push(2);
emptyTheStack(s1);
Stack
int i = 3;
int j = 4;
s2.push(&i);
s2.push(&j);
emptyTheStack(s2);
} ///:~