Templates let you write a function or class once, generically, and have the compiler generate a specific version for whatever type is actually used — C++'s mechanism for generic programming.
template <typename T>
T maxVal(T a, T b) {
return (a > b) ? a : b;
}
maxVal(3, 7); // compiler generates an int version -> 7
maxVal(3.5, 2.1); // compiler generates a double version -> 3.5
maxVal("abc", "xyz"); // works for any type supporting >
template <typename T>
class Box {
private:
T value;
public:
Box(T v) : value(v) {}
T get() const { return value; }
};
Box<int> intBox(42);
Box<std::string> strBox("hello");
template <typename T> (or the equivalent template <class T>) declares T as a placeholder type, filled in with a concrete type each time the template is actually used — this process is called instantiation, and it happens at compile time: the compiler generates a separate, fully-typed version of maxVal for int, another for double, and so on, rather than the single runtime-checked version generics use in languages like Java.
Because instantiation happens per-type at compile time, templates carry no runtime overhead compared to writing each type-specific version by hand — this is why the entire STL, including every container and algorithm, is built on templates: std::vector<int> and std::vector<std::string> are genuinely two different compiler-generated classes, not one class working with a generic Object type.
Template errors can look intimidating (long, deeply-nested error messages) because they surface only once a template is instantiated with a specific type that turns out not to support some required operation — this is a common early frustration, but the underlying cause is almost always straightforward once you find the actual line that failed inside the wall of text.