06 Templates
06 Templates
vecto sort(
r )
begin()
end()
1
Templates
Templates allow functions and classes to be
parameterized so that the type of data being
operated upon (or stored) is received via a
parameter.
Swap(x, y);
...
Swap(w, z);
...
Swap(a, b);
4
Fortran's
solution
To interchange the values of two double variables:
Can't use the preceding function; it swaps ints not
However, overloading allows us to define multiple
doubles.
versions of the same function:
/* Function to swap two double variables.
. . .
*/
void Swap(double & first, double & second)
{
double temp = first;
first = second;
second = temp;
}
The two different Swap functions are distinguished by
the compiler according to each function's signature
Sec. 6.2
6
Fortran
And so on ... for other types of variables. &C
8
Template Mechanism
Declare a type parameter (type placeholder) and
use it in
the function instead of a specific type. This requires a
different kind of parameter list:
/* Swap template for exchanging the values of any two
objects of type parameter
the same type DataType
Receive:
first and second, two objects of same type
Pass back: first and second with values swapped
Assumes: Assignment (=) defined for type DataType
template
*/ <typename DataType > // type parameter
DataType DataType
void Swap(________ & first, ________ & second)
{ DataType
________ temp = first;
first = second;
second = temp; 9
Notes:
The word template is a C++ keyword specifying that
what follows is a pattern for a function not a
function definition.
“Normal” parameters (& arguments) appear within
function parentheses; type parameters (& arguments
for class templates) appear within angle brackets
(< > ).
Originally, the keyword class was used instead of
typename in a type-parameter list. ("class" as a
synonym for "kind" or "category"— specifies
"type/kind" of types.)
Unlike other functions, a function template cannot
be split
across files. That is, we can't put prototype in a 10
How is a Template Used?
<typename DataType> names DataType as a type parameter
— value will be determined by the compiler from the type of
the
arguments passed when Swap() is called.
Example:
#include "Time.h"
#include "Swap.h" //ONE function template definition
int main()
{
int i1, i2;
double d1, d2;
string s1, s2;
Time t1, t2;
... // Compiler generates definitions
// of Swap() with DataType replaced
Swap(i1, i2); // by int
Swap(d1, d2); // by double
11
Swap(s1, s2); // by string
General Form of Template
(simplified)
15
Example
/* Function template to find largest value of any type
(for which < is defined) stored in an array
Receive: type parameter ElementType
ElementType
array of elements of type _____________
numElements, number of values in array
Return: Largest value in array
*/
Execution:
Largest value in x is 5.5
Largest value in num is 4
17
When compiler encounters Max(x, 5), it:
1. Looks for a type parameter — finds
ElementType
2. Finds type of corresponding argument (x) —
double
3. Binds these types and generates an instance
of Max():
double Max(double array[], int numElements)
{
double biggest = array[0];
for (int i = 1; i < numElements; i++)
if (biggest < array[i])
biggest = array[i];
return biggest;
} 18
Templates with multiple
parameters
template <typename TypeParam1, typename TypeParam2, ...>
FunctionDefinition
Each type parameter must appear at least once in the function's
parameter list. Why? Compiler must be able to determine actual
type corresponding to each type parameter from a function call.
/* Function template to convert a value of any type to
another type
Receive: Type parameters Type1 and Type2
value1 of Type 1
Pass back: value2 of Type2
*/
double x = 3.14;
int ix;
Convert(x, ix);
cout << x << " " << ix << endl;
}
Sample run:
a 97
3.14 3
20
The following version of function template Convert would not be
legal:
template <typename Type1, typename Type2>
Type2 Convert(Type1 value1) // Error--Type2 not in
{ // parameter list
return static_cast<Type2>(value1);
}
Problems:
1. Changes the header file
Any program/library that uses the class must be
recompiled.
2. A name declared using typedef can have only
one meaning.
If we need stacks with different element types
e.g., a Stack of ints and a Stack of strings,
Can't overload
we must create separate stack classeslike with
functions
different names. 23
Type-Independent Container
Use a class template: the class is parameterized so that it
receives the type of data stored in the class via a
parameter (like function templates).
Receives:
/* StackT.h Type parameter
contains StackElement
a template for class Stack
___________________________________________
Basic operations . . .
*/
template
const int<typename StackElement>
STACK_CAPACITY = 128;
______________________________
class Stack
{
/***** Function Members *****/
public:
. . .
/***** Data Members *****/
private:
StackElement myArray[STACK_CAPACITY];
int myTop;
};
StackElement is a “blank” type (a type placeholder)
to be filled in later. 24
General form of class template
declaration
template <typename TypeParam > or
template <class TypeParam>
class SomeClass
{
// ... members of SomeClass ...
};
25
Instantiating class templates
To use a class template in a
program/function/library:
Instantiate it by using a declaration of the form
ClassName<Type> object;
to pass Type as an argument to the class
template definition.
"name-mangling"
Examples:
Stack<int> intSt;
Stack<string> stringSt;
Compiler will generate two distinct definitions of
Stack See Slide 33 26
— two instances — one for ints and one for
Rules for class templates
1. Definitions of member functions outside class
declaration
must be function templates.
2. All uses of class name as a type must be
parameterized.
3. Member functions must be defined in the
same file as the class declaration.
For our Stack class:
a. Prototypes of function members? No change —
rules don't apply to them.
27
b. Definitions of function members?
Rule 1: They must be defined as function
templates.
template <typename StackElement> // rule #1
// ... definition of Stack()
32
Program to test this Stack template:
#include <iostream>
using namespace std;
#include "StackT.h"
int main()
{
Stack<int> intSt; // stack of ints
Stack<char> charSt; // stack of char Output:
4
for (int i = 1; i <= 4; i++) 3
intSt.push(i); 2
cout << instSt << endl; 1
D
for (char ch = 'A'; ch <= 'D'; ch++) C
charSt.push(ch); B
cout << charSt << endl; A
}
33
Templates with ordinary
parameters
*** Templates may have more than one type parameter;
they may also have ordinary parameters.
vector sort()
36
STL's Containers (§ 6.4ff)
41
//--- Definition of display operation
template <typename StackElement>
void Stack<StackElement>::display(ostream & out) const
{
for (int pos = myVector.size() - 1; pos >= 0; pos--)
out << myVector[pos] << endl;
/* or using a reverse iterator:
for (vector<StackElement>::reverse_iterator
pos = myVector.rbegin(); pos != myVector.rend(); pos++)
out << *pos << endl;
*/
}
And there's really no need to do this, since STL has done it for us!
43
STL’s stack container
STL includes a stack container.
Actually, it is an adapter, as indicated by the fact
that one of its Errors in text:
type parameters is a container type. pp. 299 & 301
Sample declaration:
stack<int,
Basically, vector<int>
it is a class that acts as > st;
a wrapper around
another class,
providing a new user interface for that class.
int main()
{
queue<int> qint;
queue<string> qstr;
qint.push(123);
47
while (!qint.empty())// Dump contents of qint
{
cout << qint.front() << " ";
qint.pop();
}
cout << endl;
48
Deques (Read pp. 294-7)
Data Block 3
6 d[8]
5 d[9]
unused d.end()
unused
unused
55
Sort 2: Supplying a "less-than" function to use in comparing
elements
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
56
int main()
{ int ints[] = {555, 33, 444, 22, 222, 777, 1, 66};
sort(ints, ints + 8, IntLessThan);
cout << "Sorted list of integers:\n";
Display(ints, 8);
st[0].push(10); st[0].push(20);
st[1].push(30);
st[2].push(50); st[2].push(60);
Output
st[3].push(1); st[3].push(999); st[3].push(3); Stack 0:
3
sort(st.begin(), st.end()); 999
for (int i = 0; i < 4; i++) 1
{
Stack 1:
cout << "Stack " << i << ":\n";
20
st[i].display();
10
cout << endl;
} Stack 2:
} 30
Stack 3:
60
50
59