Visual walkthrough — Templates — function templates, class templates
This is a companion to the main Templates note. It leans on ideas from Compile-time vs Runtime, Function Overloading, and Type Deduction (auto). Every word below is built from zero — you need to know nothing but "a function takes inputs and returns an output."
Step 1 — Start from ONE concrete function
WHAT. We write the simplest possible function that returns the larger of two whole numbers.
int maximum(int a, int b) { return (a > b) ? a : b; }WHY start here. You cannot generalize something you have not first made concrete. Look at the
body: (a > b) ? a : b means "if a is bigger than b, hand back a, otherwise hand back
b." The little ?: is called the ternary operator — read it as a one-line "if/else that
produces a value." Notice what the body actually needs: it needs to compare two things with
>, and it needs to copy one back out. It never uses anything that is special to int.
PICTURE. The function is a box. Two int values go in the left; one int comes out the right.
The word int appears three times — coloured red — and every one of those reds is a promise
that says "only whole numbers allowed here."

Step 2 — Notice the type is the only thing that changes
WHAT. Suppose you now need the maximum of two doubles (decimals). You copy-paste and swap
every int for double:
double maximum(double a, double b) { return (a > b) ? a : b; }WHY show this. Put the two functions side by side. The bodies are byte-for-byte identical. The only difference is the three type labels. This is the whole motivation for templates: the duplicated part (the logic) should be written once; the varying part (the type) should become a knob you can turn.
PICTURE. Two boxes stacked. Their insides — the (a>b)?a:b gear — are the same green gear.
Only the input/output labels differ (int red on top, double orange below). The green gear is
what we want to keep; the coloured labels are what we want to make swappable.

Step 3 — Replace the type with a placeholder name T
WHAT. Instead of writing a fixed type, we invent a placeholder and call it T. We tell the
compiler "T is a type I'll fill in later" with a header line, and then use T wherever the type
went.
template<typename T> // "T stands for some type — to be chosen"
T maximum(T a, T b) { // the same three labels, now all T
return (a > b) ? a : b; // the green gear, untouched
}WHY this exact syntax. template<typename T> is the compiler's word for "what follows is a
blueprint, and T is its blank." The word typename here just means "T names a type." (The
parent note tells you typename and class are interchangeable here — same meaning.) The blueprint
is not code yet — it is a shape with a hole in it labelled T.
PICTURE. The same box as Step 1, but the three red int labels are replaced by three blue
T labels, and a new banner template<typename T> sits on top like a nameplate. A dashed grey
outline around the whole thing signals "this is a blueprint, not a real function — no machine code
exists yet."

Step 4 — The compiler fills the hole: instantiation
WHAT. You call the blueprint with real arguments:
maximum(3, 7); // both arguments are intThe compiler looks at the arguments 3 and 7, sees they are int, and fills the T hole with
int. Only now does real code appear.
WHY the compiler can do this alone. This is type deduction (same machinery behind
auto): the compiler reads the arguments' types and solves for T. Here
3 → int and 7 → int, so T = int. It then substitutes int for every T and generates:
int maximum(int a, int b) { return (a > b) ? a : b; }— which is exactly the function we hand-wrote in Step 1. We got it for free.
PICTURE. The blue-T blueprint on the left. An arrow labelled T = int flows right into a
solid (no longer dashed) red box — a real, callable int function. The arrow is the act of
instantiation; the solid box is the stamped-out cookie.

Step 5 — One blueprint, many cookies (and none for unused types)
WHAT. Now call it with more types:
maximum(3, 7); // T = int -> stamps an int version
maximum(2.5, 1.1); // T = double -> stamps a double version
maximum<char>('a','z'); // T = char, forced explicitlyWHY the third one looks different. maximum<char>(...) puts the type inside the angle
brackets by hand instead of letting the compiler guess. You do this when you want to force a
type. Everything else deduces automatically.
The crucial edge fact: a type you never call produces zero code. If your program never
does maximum on a string, no string maximum exists in the final program. The blueprint is
"pay only for what you use."
PICTURE. One blue blueprint in the centre. Three solid boxes fan out from it — red int,
orange double, green char — each labelled with its T = .... A greyed-out, crossed-through
ghost box labelled string (never called) sits off to the side to show it does not get made.

Recall
If a program calls maximum only on int and double, how many real functions get generated? ::: Exactly two — one for int, one for double. Unused types generate nothing.
Step 6 — The degenerate case: conflicting deductions
WHAT. Try mixing types in one call:
maximum(3, 2.5); // 3 is int, 2.5 is doubleThis fails to compile.
WHY it fails. Deduction must produce one single T. The first argument argues "T should
be int"; the second argues "T should be double." The compiler cannot obey both, so it refuses
— it will not silently pick one for you. This is a feature: it stops you from a hidden, possibly
wrong, conversion.
Two fixes:
maximum<double>(3, 2.5); // force T = double; the int 3 converts to 3.0or make the two parameters independent types:
template<typename T1, typename T2>
auto maximum(T1 a, T2 b) { return (a > b) ? a : b; } // C++14 deduces the return typePICTURE. The blueprint with its single T hole. Two arrows point into that one hole: a red
one carrying int, an orange one carrying double. They collide at the hole and a red ✗ flags the
conflict — "one hole, two incompatible demands."

Step 7 — Same machine for classes: the Box<T> blueprint
WHAT. The identical idea applies to whole classes. A class bundles data + functions. We make one whose stored type is a hole:
template<typename T>
class Box {
T value; // the stored thing is of the hole type T
public:
Box(T v) : value(v) {} // build a Box from a T
T get() const { return value; }
};WHY you must say Box<int>, not just Box. A function template can look at its arguments to
deduce T. A class template (before C++17) has nothing to look at when you first name the type, so
you fill the hole yourself:
Box<int> bi(42); // instantiates a class Box<int>
Box<string> bs("hi"); // instantiates a SEPARATE class Box<string>Box<int> and Box<string> are different types — as unrelated as int and string. They
share no runtime code. This is exactly how vector<T> works.
PICTURE. One blue Box<T> blueprint holding a T-shaped slot. Two solid class boxes stamped
from it: a red Box<int> holding 42, and a green Box<string> holding "hi". A wall between them
labelled "unrelated types — no shared code."

The one-picture summary
WHAT the whole page said, in one frame. Concrete function → notice only the type varies → punch
a T hole to make a blueprint → the compiler deduces or is told T → it stamps out one
real cookie per distinct type used, and nothing for unused types; mixing types in one hole is the
one degenerate case that breaks deduction.

Recall Feynman: retell the whole walkthrough in plain words
I started with a machine that only chews on whole numbers. I noticed that if I wanted it to chew
decimals, I'd copy the whole machine and just repaint the "whole numbers" signs to say "decimals"
— the guts never changed. So I peeled off the signs entirely and hung a blank sign that says
"T — some type, TBD," and I stuck a nameplate template<typename T> on the front. That blank
machine isn't real yet; it's a drawing of a machine. Then, the moment I actually feed it two whole
numbers, the factory (the compiler) reads the numbers, writes int on all the blanks, and welds
together a real whole-number machine on the spot. Feed it decimals somewhere else and it welds a
second, separate decimal machine. If I never feed it text, no text machine is ever built — I only
pay for what I use. The one thing that jams the factory is handing the same blank a whole number
in one hand and a decimal in the other: the blank can only become one type, so the factory stops
and asks me to decide. Classes work the same way, except I have to tell the factory the type up
front by writing Box<int>, because a class has no arguments to peek at.