Code that works for many types.
For example reversing a vector looks much the same whatever the types of the elements.
void swap(int & x, int & y) {
int tmp = x; x = y; y = tmp;
}
Swapping a pair of strings is very similar:
void swap(string & x, string & y) {
string tmp = x; x = y; y = tmp;
}
And so on for every other type.
Idea: make the type a parameter, and instantiate it to int, string or any other type.
template <typename T>
void swap(T & x, T & y) {
T tmp = x; x = y; y = tmp;
}
Here T is a type parameter.
When we use this function, T is instantiated to the required type:
int i, j;
swap(i, j); // T is int
string s, t;
swap(s, t); // T is string
but in each use T must stand for a single type.
template <typename T>
and then T stands for a type,
which will be supplied when the function or class is used.
template <typename Key, typename Value>
void reverse(vector<int> & v) {
int l = 0;
int r = v.size()-1;
while (l < r) {
swap(v[l], v[r]);
l++;
r--;
}
}
Reversing a vector of strings is the same,
except for string instead of int as the element type.
template <typename Elem>
void reverse(vector<Elem> & v) {
int l = 0;
int r = v.size()-1;
while (l < r) {
swap(v[l], v[r]);
l++;
r--;
}
}
Possible strategy: write a specific version and then generalize.
vector<int> vi;
vector<string> vs;
...
reverse(vi); // T = int
reverse(vs); // T = string
This works for any type:
vector<vector<int> > vvi;
...
reverse(vvi); // T = vector<int>
Caution: these methods are only instantiated (and fully checked) when used.
template <typename Elem>
bool member(Elem x, const vector<Elem> & v) {
for (int i = 0; i < v.size(); i++)
if (v[i] == x)
return true;
return false;
}
The generic definition of member only makes sense if
the operator == is defined for T.
template <typename A, typename B>
class pair {
public:
A first;
B second;
pair(const A& a, const B& b) :
first(a), second(b) {}
};
Some pair objects:
pair<int, int> p(3, 4);
pair<int, string> n(12, "twelve");
Note that we must specify the type arguments.
template <typename T>
class vector {
public:
vector();
vector(int initial_size);
int size() const;
void clear();
T & operator[](int index) const;
T & front() const { return operator[](0); }
T & back() const { return operator[](size() - 1); }
void push_back(const T & x);
void pop_back();
};
template <typename T> class list {
public:
list();
int size() const;
void clear();
T & front() const;
void push_front(const T & x);
void pop_front();
T & back() const;
void push_back(const T & x);
void pop_back();
};
list<string> stack;
string s;
while (getline(cin, s))
stack.push_back(s);
while (stack.size() > 0) {
cout << stack.back() << '\n';
stack.pop_back();
}
int size() const;
void clear();
with appropriate properties.
T & front() const;
T & back() const;
void push_back(const T & x);
void pop_back();
Examples are Container, Sequence and Associative Container.
For example, vector is a model of Container and Sequence.
For example, Sequence is a refinement of Container.
template <typename Item>
class stack {
vector<Item> v;
public:
bool empty() const { return v.size() == 0; }
void push(const Item & x) { v.push_back(x); }
Item & top() { return v.back(); }
void pop() { v.pop_back(); }
};
template <typename Item>
class stack {
vector<Item> v;
public:
Item & top();
...
};
The method definition must then be qualified with the class name,
including parameter(s):
template <typename Item>
Item & stack<Item>::top() { return v.back(); }
map<string, int> days;
days["January"] = 31;
days["February"] = 28;
days["March"] = 31;
...
string m;
cout m << " has " << days[m] << " days\n";
cout << "There are " << days.size() << " months\n";
This is a mapping from strings to integers.
template <typename Key, typename Value>
class map {
map();
int size() const;
void clear();
int count(Key k); // 0 or 1
Value & operator[](Key k);
};
The expression m[k]
creates an entry for k if none exists.