templates are the foundation of generic programming, which involves writing code in a way that is independent of any particular type.
a template is a blueprint or formula for creating a generic class or a function. the library containers like iterators and algorithms are examples of generic programming and have been developed using template concept.
there is a single definition of each container, such as vector, but we can define many different kinds of vectors for example, vector <int> or vector <string>.
you can use templates to define functions as well as classes, let us see how they work −
function template
the general form of a template function definition is shown here −
template <class type> ret-type func-name(parameter list) {
// body of function
}
here, type is a placeholder name for a data type used by the function. this name can be used within the function definition.
the following is the example of a function template that returns the maximum of two values −
#include <iostream>
#include <string>
using namespace std;
template <typename t>
inline t const& max (t const& a, t const& b) {
return a < b ? b:a;
}
int main () {
int i = 39;
int j = 20;
cout << "max(i, j): " << max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "max(f1, f2): " << max(f1, f2) << endl;
string s1 = "hello";
string s2 = "world";
cout << "max(s1, s2): " << max(s1, s2) << endl;
return 0;
}
if we compile and run above code, this would produce the following result −
max(i, j): 39 max(f1, f2): 20.7 max(s1, s2): world
class template
just as we can define function templates, we can also define class templates. the general form of a generic class declaration is shown here −
template <class type> class class-name {
.
.
.
}
here, type is the placeholder type name, which will be specified when a class is instantiated. you can define more than one generic data type by using a comma-separated list.
following is the example to define class stack<> and implement generic methods to push and pop the elements from the stack −
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class t>
class stack {
private:
vector<t> elems; // elements
public:
void push(t const&); // push element
void pop(); // pop element
t top() const; // return top element
bool empty() const { // return true if empty.
return elems.empty();
}
};
template <class t>
void stack<t>::push (t const& elem) {
// append copy of passed element
elems.push_back(elem);
}
template <class t>
void stack<t>::pop () {
if (elems.empty()) {
throw out_of_range("stack<>::pop(): empty stack");
}
// remove last element
elems.pop_back();
}
template <class t>
t stack<t>::top () const {
if (elems.empty()) {
throw out_of_range("stack<>::top(): empty stack");
}
// return copy of last element
return elems.back();
}
int main() {
try {
stack<int> intstack; // stack of ints
stack<string> stringstack; // stack of strings
// manipulate int stack
intstack.push(7);
cout << intstack.top() <<endl;
// manipulate string stack
stringstack.push("hello");
cout << stringstack.top() << std::endl;
stringstack.pop();
stringstack.pop();
} catch (exception const& ex) {
cerr << "exception: " << ex.what() <<endl;
return -1;
}
}
if we compile and run above code, this would produce the following result −
7 hello exception: stack<>::pop(): empty stack