The difficulty in comparing them is mostly due to the fact that the
C++ standards committee was careless in the template specification
and accidentally got something a lot more powerful than they expected.
As a result, templates are a very weird language construct -- they
were intended to make parametric polymorphism easier to do but
are actually capable of a lot more than that.
Parametric polymorphism is the ability to write a function that
operates the same way on multiple types. The simplest example is
the identity function
# let identity x = x
val identity : 'a -> 'a
The function identity simply returns its argument, and the type 'a -> 'a
indicates that it can accept any type argument. Similarly, you can
write a polymorphic length function for lists like this:
# let rec len x =
match x with
[] -> 0
| x :: xs -> 1 + len xs
val len: 'a list -> int
Here, you can see that len operates on lists of any element -- that's what
the type 'a list means. The translations for identity and len in C++ are
two of the first examples people see when using templates.
Note that there are no type declarations in the OCaml code; the compiler
automatically infers the most general typing for every expression
submitted to the compiler, and will signal a compile error if there is
no correct type for the code. This is different from C++, where every
type has to be explicitly declared, and every polymorphic function
needs to be enclosed in an explicit template expression.
Note that parametric polymorphism is a very specific kind of polymorphim:
a polymorphic function does exactly the same thing regardless of the
types of the arguments. Sometimes you need to parameterize your code
so that it does different things at different types. This is what ML
offers functors for -- they let you write modules that are parameterized
with other modules.
C++ uses templates plus inheritance to model this case, plus some linking games. This is IME rather complicated, and doesn't map to how people
think about it. Not a fatal problem, but definitely an annoyance. The
real killer argument for me is that functors and parametric polymorphism
do not break separate compilation and do not cause code bloat. Due to
the way they are specified, a compiler can generate a single piece of
code for each polymorphic function and each functor.