C++
Templates
Without templates, writing a function that finds the maximum of two values means writing it once per
type — maxInt, maxFloat, maxDouble — identical logic, copy-pasted.
A template lets you write the logic exactly once, generic over the type, and have the compiler generate
the type-specific version automatically wherever it's actually used.
- What it is
- A blueprint for a function or class that's generic over one or more types, filled in by the compiler at compile time for each type it's actually used with.
- Mental model
- A cookie cutter shape (the template) that gets stamped out in a specific material (a specific type) only when actually needed — the compiler does the stamping, once per type, automatically.
- Key numbers
- Zero runtime cost — template code is fully resolved to real, concrete, type-specific code at compile time, identical in performance to hand-writing each version yourself.
- Reach for it when
- The exact same logic needs to work across multiple types — a container, a comparison function, a math utility — and hand-duplicating it per type would be real, repeated boilerplate.
Function templates
template <typename T>
T maxValue(T a, T b) {
return (a > b) ? a : b;
}
int i = maxValue(3, 7); // T becomes int — no cast, no separate function needed
float f = maxValue(2.5f, 1.1f); // T becomes float
double d = maxValue(3.14, 2.71); // T becomes double
template <typename T> declares T as a placeholder type — the compiler
figures out what T should be from the arguments each specific call actually uses, with no
extra syntax required at the call site.
How the compiler actually handles this
Class templates
Classes can be templated too — this is exactly how generic containers work, previewed here and covered fully in the next chapter, STL Containers & Algorithms:
template <typename T>
class Box {
public:
Box(T value) : value(value) {}
T get() { return value; }
private:
T value;
};
Box<int> intBox(42);
Box<std::string> strBox("hello"); // the SAME Box class, holding a completely different type
Templates vs. C-style generic macros
C has no templates — the closest equivalent is a function-like macro, which is pure text substitution with no type checking at all:
// C: a macro "generic" max — no type safety, evaluates arguments TWICE (see C's Preprocessor gotchas)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
// C++: a real template — type-checked, each argument evaluated exactly once
template <typename T>
T maxValue(T a, T b) { return (a > b) ? a : b; }
A template is genuinely type-checked by the compiler and behaves like a normal function in every way that matters (arguments evaluated once, real scoping) — strictly safer than the macro equivalent, not just more convenient syntax.
Real-world applications
- The entire STL — every standard container (
vector,map) and algorithm (sort,find) is a template, generic over whatever type you actually store — the subject of the next chapter. - Generic math/utility libraries — a templated
clamp(value, min, max)function working identically acrossint,float, and any other comparable type. - Type-safe generic data structures — a templated ring buffer or fixed-size queue, reused across a firmware codebase for any element type without duplicating the logic per type.
Gotchas
- A template error often produces a long, intimidating compiler error message pointing deep into template internals — reading these gets easier with practice, but they're a genuine rough edge of the language, especially for beginners.
- Each distinct type a template is instantiated with generates its own separate compiled code — heavy template use across many types can meaningfully increase binary size, worth watching on a flash-constrained embedded target.
- Template code effectively lives in the header file, not a separate
.cpp— the compiler needs to see the full template definition everywhere it's instantiated, which changes the usual header/source split from Compilation & Linking. - Not every type works with every template —
maxValueabove requires>to be defined for whatever typeTis; calling it with a type that has no>operator fails to compile, with an error that can be hard to parse at first.