As you can see, you can partially specify template parameters according to whether they are pointer types, or whether they are equal. When the T* specialization is used, such as is the case in line 5, T itself is not the top-level pointer type that was passed—it is the type that the pointer refers to (float, in this case). The T* specification is a pattern to allow matching against pointer types. If you were to use int** as the first template argument, T would be int*. Line 8 is ambiguous because having the first parameter as an int vs. having the two parameters equal are independent issues—one is not more specialized than the other. Similar logic applies to lines 9 through 12.
A practical example
You can easily derive from a class template, and you can create a new template that instantiates and inherits from an existing template. If the vector template does most everything you want, for example, but in a certain application you’d also like a version that can sort itself, you can easily reuse the vector code. The following example derives from vector
//: C05:Sorted.h
// Template specialization
#ifndef SORTED_H
#define SORTED_H
#include
#include
template
class Sorted : public std::vector
public:
void sort();
};
template
void Sorted
for(int i = size(); i > 0; i--)
for(int j = 1; j < i; j++)
if(at(j-1) > at(j)) {
T t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
// Partial specialization for pointers:
template
class Sorted
public:
void sort();
};
template
void Sorted
for(int i = size(); i > 0; i--)
for(int j = 1; j < i; j++)
if(*at(j-1) > *at(j)) {
T* t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
// Full specialization for char*
// (Made inline here for convenience –
// normally would place function body in separate file
// and only leave declaration here)
template<>
inline void Sorted
for(int i = size(); i > 0; i--)
for(int j = 1; j < i; j++)
if(std::strcmp(at(j-1), at(j)) > 0) {
char* t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
#endif // SORTED_H ///:~
The Sorted template imposes a restriction on all but one of the classes for which it is instantiated: they must contain a > operator. It works correctly only with non-pointer objects (including objects of built-in types). The full specialization compares the elements using strcmp( ) to sort vectors of char* according to the null-terminated strings to which they refer.
Here’s a driver for Sorted.h that uses the randomizer introduced earlier in the chapter:.
//: C05:Sorted.cpp
//{bor} (because of bitset in Urand.h)
// Testing template specialization
#include
#include
#include "Sorted.h"
#include "Urand.h"
using namespace std;
#define asz(a) (sizeof a / sizeof a[0])
char* words[] = {
"is", "running", "big", "dog", "a",
};
char* words2[] = {
"this", "that", "theother",
};
int main() {
Sorted
Urand<47> rand;
for(size_t i = 0; i < 15; i++)
is.push_back(rand());
for(size_t i = 0; i < is.size(); i++)
cout << is[i] << ' ';
cout << endl;
is.sort();
for(size_t i = 0; i < is.size(); i++)
cout << is[i] << ' ';
cout << endl;
// Uses the template partial specialization:
Sorted
for(size_t i = 0; i < asz(words); i++)
ss.push_back(new string(words[i]));
for(size_t i = 0; i < ss.size(); i++)
cout << *ss[i] << ' ';
cout << endl;
ss.sort();
for(size_t i = 0; i < ss.size(); i++) {
cout << *ss[i] << ' ';
delete ss[i];
}
cout << endl;
// Uses the full char* specialization:
Sorted
for(size_t i = 0; i < asz(words2); i++)
scp.push_back(words2[i]);
for(size_t i = 0; i < scp.size(); i++)
cout << scp[i] << ' ';
cout << endl;
scp.sort();
for(size_t i = 0; i < scp.size(); i++)
cout << scp[i] << ' ';
cout << endl;
} ///:~
Each of the template instantiations above uses a different version of the template. Sorted
Preventing template code bloat
Whenever a class template is instantiated, the code from the class definition for the particular specialization is generated, along with all the member functions that are called in the program. Only the member functions that are actually called are generated. This is a good thing, as the following program makes clear:.
//: C05:DelayedInstantiation.cpp
// Member functions of class templates are not
// instantiated until they're needed.
class X {
public:
void f() {}
};
class Y {
public:
void g() {}
};
template
T t;
public:
void a() { t.f(); }
void b() { t.g(); }
};
int main() {
Z
zx.a(); // Doesn't create Z
Z