The simplest example of a template is this:
1 2 3 4 5

template <class T>
T max(T a, T b)
{
return (a < b) ? b : a;
}

Functionally, it's almost the same as this:
#define max(a,b) (((a)<(b))?(b):(a))
It's almost the same in that both return the max and both are somewhat weakly typed. However, there are reasons why the templated version is safter than the define version. Take this for example:
int i = max( a, b++);
If we use the templated function, b is certainly incremented once. In the #define, b might be incremented twice because it's direct text replacement.
There are also much more complicated ways to use templates. I like to use std::enable_if to make templated functions only available to certain types or data, or sizes. Specialization is also a good use here. In fact templates are so powerful that I've seen code which fools your compiler into creating a list of prime numbers during compile time with templates.
Edit: Went to search for a TMP prime calculator. The first google result was this with an answer from someone named
Cubbi 4 years ago. Is this our
Cubbi? Now we know what you look like!
http://answers.yahoo.com/question/index?qid=20100510032851AAubEzZ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

#include <iostream>
using namespace std;
// primality test
template <int p, int i>
class is_prime {
public:
enum { prim = (p==2)  (p%i && is_prime< (i>2?p:0), i1>::prim ) };
};
template<> class is_prime<0,0> { public: enum {prim=1}; };
template<> class is_prime<0,1> { public: enum {prim=1}; };
// loop to print N prime numbers greater or equal to i
template <int i, int N>
class Prime_print {
public:
Prime_print<i+1, N  is_prime<i,i1>::prim> a;
enum { prim = is_prime<i,i1>::prim };
void f() { if(prim) cout << i << endl; a.f(); }
};
template<int i> class Prime_print<i, 0> { public: enum {prim=0}; void f() {} };
int main()
{
const int N = 10;
Prime_print<2, N> a;
a.f();
}
