This page was last updated August, 2004
Templates
Template: A block of code using specific syntax that allows the compiler
to modify the code for use with different data types. Templates are an attempt to
incorporate Code Reuse into the language. C++ now includes a Standard Template Library.
You can create templates for individual functions and for whole classes. Consider again how
function overloading works and use the example of these
overloaded functions:
int Multiply(int arg1, int arg2);
long Multiply(long arg1, long arg2);
float Multiply(float arg1, float arg2);
double Multiply(double arg1, double arg2);
If we could define this function without specifying the data type and then let the compiler generate the
appropriate code for whatever data type we need then we would only have to write the code once.
That is, suppose we could define a single function like this:
datatype Multiply(datatype arg1, datatype arg2)
{
return arg1 * arg2;
}
Now if we could tell the compiler to create a function but substitute "int" for
"datatype" and create a function but substitute "float" for "datatype", etc. we
would have all of our functions but only have to write the code once.
This is exactly what Templates allow us to do:
Here is the Function Template for the Multiply() function:
template <class datatype>
datatype Multiply(datatype arg1, datatype arg2)
{
return arg1 * arg2;
}
Look at this template line by line.
template <class datatype>
This tells the compiler that the following function is a template that
will use a "class" called "datatype" that will be defined at compile time.
datatype Multiply(datatype arg1, datatype arg2)
This is like a normal function definition but with the word "datatype"
substituted for the actual datatype name. Note that "datatype" is not
a keyword. You can use any word you want to as your class name. The
rest of the function definition is normal C++ code.
Here is a sample of some actual code using this template.
//==========================================================
// File: FuncTemplate.cpp
// Purpose: Demonstrate function templates
// Author: Dr. Rick Coleman
//==========================================================
#include <iostream.h>
//---------------------------------------
// Template for Multiply() function
//---------------------------------------
template <class datatypeA, class datatypeB>
datatypeA Multiply(datatypeA arg1, datatypeB arg2)
{
return arg1 * arg2;
}
int main(int argc, char **argv)
{
cout << "Int (Int 2 * Long 2) = " << Multiply(2, 2) << "\n";
cout << "Long (Long 2 * Int 2) = " << Multiply(2L, 2) << "\n";
cout << "Float (Int 2 * Float 2) = " << Multiply(2, 2.0f) << "\n";
cout << "Double (Float 2 * Double 2) = " << Multiply(2.0f, 2.0) << "\n";
return 0;
}
In this example the compiler creates 4 different versions of the Multiply()
function from the template just as if they had been typed in as overloaded
functions.
You can also specify different argument types as in the next example:
//==========================================================
// File: FuncTemplate.cpp
// Purpose: Demonstrate function templates
// Author: Dr. Rick Coleman
//==========================================================
#include <iostream.h>
//---------------------------------------
// Template for Multiply() function
//---------------------------------------
template <class datatypeA, class datatypeB>
datatypeA Multiply(datatypeA arg1, datatypeB arg2)
{
return arg1 * arg2;
}
int main(int argc, char **argv)
{
cout << "Int (Int 2 * Long 2) = " << Multiply(2, 2) << "\n";
cout << "Long (Long 2 * Int 2) = " << Multiply(2L, 2) << "\n";
cout << "Float (Int 2 * Float 2) = " << Multiply(2, 2.0f) << "\n";
cout << "Double (Float 2 * Double 2) = " << Multiply(2.0f, 2.0) << "\n";
return 0;
}
Class Templates
This idea of templates can be expanded to include templates of entire classes.
Consider a simple linked list class using a node template and a class template.
//========================================================
// ListTemplate.h
// Header and implementation file defining a linked list
// template class to be used in a linked list.
// Author: Dr. Rick Coleman
//========================================================
#ifndef LISTTEMPLATE_H
#define LISTTEMPLATE_H
template <class NodeType, class KeyType>
class ListTemplate
{
protected:
NodeType *head;
NodeType *tail;
public:
ListTemplate(); // Class constructor
~ListTemplate(); // Class destructor
void Insert(NodeType *nt); // Add a node to the list
void Delete(KeyType key); // Delete a node from the list
NodeType *Search(KeyType key); // Search given a key
void PrintKeys(); // Print all keys in the list for testing
};
//---------------------------------------
// Class constructor
//---------------------------------------
template <class NodeType, class KeyType>
ListTemplate<NodeType, KeyType>::ListTemplate()
{
head = tail = NULL;
}
//---------------------------------------
// Class destructor
//---------------------------------------
template <class NodeType, class KeyType>
ListTemplate<NodeType, KeyType>::~ListTemplate()
{
NodeType *temp;
temp = head;
while(temp != NULL)
{
head = head->next;
delete temp;
temp = head;
}
}
//---------------------------------------
// Insert a node into the list
//---------------------------------------
template <class NodeType, class KeyType>
void ListTemplate<NodeType, KeyType>::Insert(NodeType *nt)
{
NodeType *temp, *back;
KeyType key;
if(head == NULL)
{
// Insert first node in the new list
head = tail = nt;
}
else
{
// Find location for new node in the list
temp = head;
back = NULL;
key = nt->getKey();
while((temp != NULL) && (temp->getKey() < key))
{
back = temp;
temp = temp->next;
}
// Check to see if adding at head of list
if(back == NULL)
{
nt->next = head;
nt->prev = NULL;
head = nt;
}
else // Insert somewhere else in list
{
back->next = nt;
nt->next = temp;
nt->prev = back;
if(temp == NULL)
{
tail = nt;
}
else
{
temp->prev = nt;
}
}
}
}
//---------------------------------------
// Delete a node from the list
//---------------------------------------
template <class NodeType, class KeyType>
void ListTemplate<NodeType, KeyType>::Delete(KeyType key)
{
NodeType *temp, *back;
// Check for empty list
if(head == NULL) return; // Nothing to delete
// Search the list for the item to delete
temp = head;
back = NULL;
while((temp != NULL) && (key != temp->getKey()))
{
back = temp;
temp = temp->next;
}
// Check to see if the item was found
if(temp == NULL) return; // Not found so return
else if(back == NULL) // Check to see if item is first in list
{
head = head->next;
delete temp; // Dispose of the node removed from the list
if(head == NULL) tail = NULL;
else head->prev = NULL;
}
else // Delete node elsewhere in the list
{
back->next = temp->next;
if(back->next == NULL) tail = back;
else temp->next->prev = back;
delete temp; // Dispose of the node removed from the list
}
}
//---------------------------------------
// Search the list
//---------------------------------------
template <class NodeType, class KeyType>
NodeType *ListTemplate<NodeType, KeyType>::Search(KeyType key)
{
cout << "In Search function\n";
return NULL;
}
//---------------------------------------
// Print all keys in the list for testing
// purposes.
//---------------------------------------
template <class NodeType, class KeyType>
void ListTemplate<NodeType, KeyType>::PrintKeys()
{
NodeType *temp;
temp = head;
int count = 0;
while(temp != NULL)
{
cout << "Key #" << count << " = " << temp->getKey() << "\n";
temp = temp->next;
count++;
}
}
#endif // End of list template implementation
//==========================================================
// main.cpp
// Main program used to test a demonstration class template.
// Author: Dr. Rick Coleman
//==========================================================
#include
#include "NodeTemplate.h"
#include "ListTemplate.h"
struct testStruct
{
int key;
double f;
};
int main(int argc, char **argv)
{
testStruct *t;
NodeTemplate<testStruct, int> *NT;
ListTemplate<NodeTemplate, int> theList;
// Insert first test node into the list
t = new testStruct();
t->key = 32;
t->f = 3.14;
NT = new NodeTemplate;
NT->setData(t);
theList.Insert(NT);
// Insert second test node into the list as first node
t = new testStruct();
t->key = 8;
t->f = 2.1;
NT = new NodeTemplate;
NT->setData(t);
theList.Insert(NT);
// Insert third test node into the list as a middle node
t = new testStruct();
t->key = 16;
t->f = 4.5;
NT = new NodeTemplate;
NT->setData(t);
theList.Insert(NT);
// Insert fourth test node into the list as last node
t = new testStruct();
t->key = 48;
t->f = 8.2;
NT = new NodeTemplate;
NT->setData(t);
theList.Insert(NT);
cout << "Done testing insertion of nodes into list\n";
// Print all the keys to be sure everything is there
theList.PrintKeys();
cout << "Testing deleting middle and head nodes.\n";
theList.Delete(32);
theList.Delete(8);
cout << "Done testing deletion.\n";
// Print all the keys to be sure everything is there
theList.PrintKeys();
return 0;
}
Note: As of this writing there is no easy way to separate the interfact (header)
from the implementation (.cpp) so both are placed in the same file.
Note: The node template will only work with numeric keys, i.e. short, int, long, f
loat, double, or char. It will not work with character arrays (strings) as keys.
C++ Standard Template Library --
C++ contains a library (that is still very much under development and revision)
that contains many templates. It includes templates for Sets, Stacks, Queues,
Strings, and Vectors among others.