Templates
Type-independent recipes for
function or class generation.
No instantiation of code
(for a function or class) until needed.
Template Functions
The classic example. . .
at various times within a single program,
2 values need to be swapped.
One solution:
void swap(int & a, int & b) {
int temp;
temp = a;
a = b;
b = temp;
}
This function is needed for more than
the int type.
So, overload by different signatures
(and associated, but not shown, definitions):
void swap(int & a, int & b); void swap(double & a, double & b); void swap(char & a, char & b); void swap(myClass & a, myClass & b);
Overloading gets tedious for this case.
The type changes, but none of the code changes!
Template functions solve this problem.
template <typename Any>
void swap(Any & a, Any & b) {
Any temp;
temp = a;
a = b;
b = temp;
}
Note: older versions of C++ code began with
template <class Any>
Note: many programmers prefer to use
T instead of Any
A use of the template function,
int x, y; swap(x, y);
causes a creation (instantiation) of the code:
void swap(int & a, int & b) {
int temp;
temp = a;
a = b;
b = temp;
}
Yes, if we have the code,
myClass x, y; swap(x, y);
this creates (instantiates) another
version of the code:
void swap(myClass & a, myClass & b) {
myClass temp;
temp = a;
a = b;
b = temp;
}
Hopefully, we all realize that the
amount of code
created is no worse than writing
all those overloaded functions for each
needed type.
A benefit of template functions is that
if a new type must be accomodated,
a recompilation instantiates the new code,
without writing more code!
A class can be specified by a template.
The classic examples are container classes.
For example: a stack
Items that we will put into a stack are
all of the same type.
But we may want several stacks, and each stack
may hold items of a different type.
Note that the code is the same,
except for the type of the item that the stack holds,
for each of these stacks.
A parameterized type is "passed" to the template
as an "argument."
Part of the template class declaration:
template <class T>
class Stack {
public:
bool push(const T & item);
bool pop(T & item);
private:
T arr[MAX];
int tos;
}
Just one of the member function definitions:
template <class T> bool Stack::push(const T & item) { if ( tos < MAX ) { arr[tos++] = item; return true; } else { return false; } }
Typically: the entire template class
declaration and definition
go into the header file.
If we want the definition in a separate file,
then each definition begins with
(constructor example)
export template <class T> Stack::Stack()
Note: only newer C++ compilers incorporate
the export syntax.
Using (causing the instantiation)
of a Stack < > class.
#include "stacktemplate.h" . . Stack<int> my_stack1;
This incorporates the code,
with T replaced by int.
Another instance of the entire class
is made due to the declaration:
Stack<cookies> party;
Extra arguments to the template class
are allowed.
A portion of an example where one of these
non-type arguments or expression arguments
is the size of the "container" class:
template <class T, int n>
class Container {
public:
.
.
private:
T items[n];
.
.
}
A declaration which instantiates the class:
Container<double, 16> my_container;
Limitations on expression arguments:
int, unsigned, enum),
n++ not allowed.)
Not enough time to present:
Inheritance and Templates:
class Derived: public Stack<T> {
}
template <class T>
class Derived: public Stack<T> {
}
Not enough time to present:
We can nest template instantiations:
Stack < Stack <int> > stackofstacks;
This asks for a stack,
where each element pushed is a stack of ints.
Not enough time to present:
We can have
more than 1 class/typename parameter:
template <class T1, class T2>
class TwiceTheFun {
public:
private:
T1 a; // a field may use the type
T2 b;
}
We may use the 2 parameters independently.
Not enough time to present:
We can provide
a default for a class/typename parameter:
template <class T1, class T2 = int>
class TwiceTheFun {
public:
private:
T1 a; // a field may use the type
T2 b;
}
Specializations
Defines which class is instantiated/used
when multiple possibilities exist.
All examples shown so far (such as)
Stack <int> my_stack1;are implicit instantiations.
Recall that a template instantiation
causes a type-specific version of the
code to be created.
If we want to explicitly ask for
a type-specific version of the code to exist:
template class Stack<double>;
This is an explicit instantiation.
Use an explicit specialization,
if different behavior is needed for different
parameter types.
template <typename T>
const T & min(const T & x, const T & y) {
return (x < y) ? x : y;
}
template <>
const double & min<double>(const double & x,
const double & y) {
// do something special for doubles
}
Not enough time to present:
| Copyright © Karen Miller, 2007 |