4.1 Function Templates & Class Templates
What is Generic Programming?
Generic programming writes algorithms and data structures independent of the data type they operate on. The same code works for int, double, string, user-defined types.
C++ achieves genericity via templates — a form of parametric polymorphism resolved at compile time.
template<typename T>
T add(T a, T b) {
return a + b;
}
add(3, 5); // T = int
add(2.5, 3.5); // T = double
add(string("Hi "), string("World")); // T = string
The compiler generates one version per type — zero runtime cost.
---
Function Templates
A function template is a blueprint from which the compiler generates concrete functions when called with specific types.
Syntax
template<typename T> // T is the template parameter
returnType name(parameters) {
// body using T
}
typename and class are interchangeable here:
template<class T> // same as typename
Example — generic max
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << max(3, 5); // 5
cout << max(2.5, 1.5); // 2.5
cout << max(string("apple"), string("banana")); // banana
return 0;
}
How template instantiation works
At each call site, the compiler:
- Deduces
Tfrom the argument types - Instantiates a concrete function with
Treplaced - Compiles that function
max(3, 5);
// Compiler generates:
// int max(int a, int b) { return (a > b) ? a : b; }
Multiple template parameters
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) { // C++11 trailing return type
return a + b;
}
add(3, 5.5); // T=int, U=double, return = double
In C++14+, simpler:
template<typename T, typename U>
auto add(T a, U b) { return a + b; }
Explicit template argument
If type deduction fails or you want to force a type:
add<double>(3, 5); // force T = double
---
Characteristics of Function Templates
| Property | Detail |
|---|---|
| One template, many functions | Compiler instantiates on demand |
| Type-deduction | Usually automatic from arguments |
| Compile-time | All resolution happens at compile time |
| Zero overhead | No runtime cost — like writing each version by hand |
| Each instantiation = full compile | More instantiations = more compile time + larger binary |
| Type must support required operations | If T is used with >, T must have operator> |
---
Overloading Template Functions
You can overload a template with a non-template, or specialise:
1. Non-template overload (takes precedence)
template<typename T>
void print(T x) { cout << "template: " << x << endl; }
void print(int x) { cout << "non-template int: " << x << endl; } // overload
print(5); // calls non-template int version (preferred)
print(5.5); // calls template (T = double)
print('A'); // calls template (T = char)
2. Template specialisation (custom behaviour for specific type)
template<typename T>
void print(T x) { cout << "Generic: " << x << endl; }
template<>
void print<string>(string s) { cout << "String: \"" << s << "\"" << endl; }
print(5); // Generic
print(string("hello")); // String: "hello"
3. Template overload with different parameter counts
template<typename T>
T add(T a, T b) { return a + b; }
template<typename T>
T add(T a, T b, T c) { return a + b + c; }
add(1, 2); // 2-arg version
add(1, 2, 3); // 3-arg version
---
Class Templates
A class template parameterises a class by one or more types:
template<typename T>
class Box {
private:
T content;
public:
Box(T c) : content(c) {}
T get() const { return content; }
void set(T c) { content = c; }
};
int main() {
Box<int> intBox(42);
Box<string> strBox("hello");
Box<double> dblBox(3.14);
cout << intBox.get() << endl; // 42
cout << strBox.get() << endl; // hello
return 0;
}
You must specify the type when instantiating: Box<int>, Box<string>.
Member functions defined outside the class
template<typename T>
class Stack {
private:
T data[100];
int top;
public:
Stack();
void push(T x);
T pop();
};
// Implementation OUTSIDE the class — also a template
template<typename T>
Stack<T>::Stack() : top(-1) {}
template<typename T>
void Stack<T>::push(T x) {
if (top < 99) data[++top] = x;
}
template<typename T>
T Stack<T>::pop() {
return (top >= 0) ? data[top--] : T();
}
Each external definition needs its own template<typename T> declaration.
---
Class Template — Stack Example
template<typename T>
class Stack {
private:
T* data;
int size;
int top;
public:
Stack(int s) : size(s), top(-1) {
data = new T[size];
}
~Stack() { delete[] data; }
void push(T x) {
if (top + 1 < size) data[++top] = x;
else cout << "Stack full\n";
}
T pop() {
if (top >= 0) return data[top--];
return T(); // default-constructed T
}
bool empty() const { return top == -1; }
int count() const { return top + 1; }
};
int main() {
Stack<int> s(10);
s.push(1); s.push(2); s.push(3);
while (!s.empty()) cout << s.pop() << " "; // 3 2 1
cout << endl;
Stack<string> ss(5);
ss.push("Hello");
ss.push("World");
cout << ss.pop() << endl; // World
return 0;
}
One template, infinite type combinations.
---
Multiple Template Parameters
template<typename K, typename V>
class Pair {
public:
K key;
V value;
Pair(K k, V v) : key(k), value(v) {}
void display() const {
cout << key << " => " << value << endl;
}
};
int main() {
Pair<string, int> age("Rohit", 25);
age.display(); // Rohit => 25
Pair<int, double> rate(101, 8.5);
rate.display(); // 101 => 8.5
return 0;
}
---
Default Template Arguments
template<typename T = int, int SIZE = 100>
class FixedArray {
T data[SIZE];
public:
T& operator[](int i) { return data[i]; }
};
FixedArray<> a; // T=int, SIZE=100
FixedArray<double> b; // T=double, SIZE=100
FixedArray<char, 50> c; // T=char, SIZE=50
---
Non-Type Template Parameters
Template parameters can be values (not just types):
template<int N>
class Array {
private:
int data[N];
public:
int& operator[](int i) { return data[i]; }
int size() const { return N; }
};
Array<10> a; // N = 10 — array of 10 ints
Array<100> big; // N = 100
cout << a.size(); // 10
Allowed non-type parameters: integers, pointers, references, enum values — all known at compile time.
---
Template Specialisation
Sometimes you want different behaviour for a specific type:
Full specialisation
template<typename T>
class Print {
public:
void operator()(T x) {
cout << x << endl;
}
};
template<> // full specialisation for bool
class Print<bool> {
public:
void operator()(bool x) {
cout << (x ? "true" : "false") << endl;
}
};
Print<int> pi; pi(5); // 5
Print<bool> pb; pb(true); // true (not 1)
Partial specialisation (classes only — not functions)
template<typename T, typename U>
class Pair { /* general */ };
template<typename T>
class Pair<T, T> { // partial — when both types are same
/* special behaviour */
};
Pair<int, double> p1; // general
Pair<int, int> p2; // partial specialisation
---
Templates vs Macros — exam classic
| Aspect | Macros (#define) | Templates |
|---|---|---|
| Resolution | Preprocessor — text substitution | Compiler — type-aware |
| Type safety | None | Strong |
| Debugger support | Hard | Normal |
| Error messages | Cryptic preprocessor errors | Compiler errors with template context |
| Scope | Global, no namespacing | Respects scope |
| Use | Conditional compilation, legacy | Modern generic code |
Modern C++ strongly prefers templates over macros.
---
Templates vs Function Overloading
| Aspect | Function Overloading | Function Templates |
|---|---|---|
| Multiple functions | Written by programmer | Generated by compiler |
| Implementation | Each version separately | Single generic implementation |
| Code reuse | Limited | Excellent |
| When to use | Different behaviour per type | Same behaviour across types |
| Resolution | Compile-time | Compile-time |
You can combine them — overload a template with a non-template (compiler prefers the non-template if it matches).
---
Templates Power the STL
The Standard Template Library (STL) is built entirely on templates:
vector<int> ints;
vector<string> words;
map<string, int> counts;
set<double> unique;
list<Student> students;
All containers, algorithms (sort, find, accumulate), and iterators are templates. Zero-overhead generic data structures and algorithms.
---
Limitations of Templates
| Limitation | Detail |
|---|---|
| Long compile times | Each instantiation = full compile |
| Larger binaries | Each unique T = separate compiled code |
| Hard error messages | Pages of template-instantiation traces |
| All code in headers | Template implementation must be visible to caller |
Type T must support operations used | If you write a > b, T must have > |
---
Modern: auto and Concepts
C++14 — generic lambdas
auto add = [](auto a, auto b) { return a + b; };
add(1, 2); // int
add(1.5, 2.5); // double
C++20 — concepts (constrained templates)
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;
template<Numeric T>
T add(T a, T b) { return a + b; }
add(1, 2); // OK
// add("a", "b"); // ERROR — string isn't Numeric
Concepts make template errors clean and intent obvious.
---
Study deep
- Templates are zero-cost. Each
vector<int>is as fast as a hand-written int-array class. The cost is compile-time + binary size — never runtime.
- Header-only is a feature. Templates live in headers because the compiler needs the full definition to instantiate. This is why STL is in headers.
- Template metaprogramming (TMP) is a Turing-complete language. Crazy power: compute factorials at compile time, generate code based on types. Used heavily in performance-critical libraries (Boost, Eigen, expression templates).
- Type deduction is mostly intuitive.
max(1, 2)→T = int.max(1, 2.0)→ ERROR (ambiguous). Forcing withmax<double>(1, 2.0)resolves it.
- Don't fear template error messages. They look intimidating but always trace back to: "on this line, you used T as if it had this operation, but the actual type doesn't". Read the bottom of the error stack, not the top.
Key Terms — Lesson 4.1
The terms below cover C++ templates — every PYQ on function/class templates or generic programming expects fluent use.
Generic Programming — Writing code that works for any type, deferring the type choice to the user. C++ implements generic programming via templates. std::sort works on int, string, custom classes — the algorithm is written once.
Template — A C++ construct that parameterises code on one or more types or values. Templates are compile-time blueprints; the compiler instantiates concrete code from the template each time it is used with specific arguments.
Function Template — A template that produces a function for each set of type arguments. template<typename T> T max(T a, T b) { return a > b ? a : b; } — the compiler generates max<int>, max<double>, etc., as needed.
Class Template — A template that produces a class for each set of type arguments. template<typename T> class Stack { ... }; — Stack<int> and Stack<string> are two different (compiler-generated) classes with the same structure.
Template Parameter — The placeholder type or value in a template declaration. Type parameter (typename T or class T) holds a type. Non-type parameter (int N, size_t size) holds a value. C++20 also adds type-constrained parameters via concepts.
typename vs class (in Templates) — Two equivalent keywords for declaring type parameters: template<typename T> and template<class T> mean the same thing. typename is preferred in modern style; class is the older keyword.
Template Instantiation — The compiler's process of generating concrete code from a template when it sees a use like vector<int>. The compiler substitutes the type arguments and compiles the resulting concrete class or function. Each unique combination of arguments produces one instantiation.
Implicit Instantiation — The compiler automatically instantiates a template the first time it sees the template used with specific arguments. max(1, 2) triggers instantiation of max<int>.
Explicit Instantiation — A way to tell the compiler to instantiate a template at a specific point even without a use: template int max<int>(int, int);. Used to control where template code appears in the binary and to reduce duplicated instantiations across translation units.
Type Deduction — The compiler's algorithm for inferring template arguments from the call. max(1, 2) infers T = int. max(1, 2.0) is ambiguous; max<double>(1, 2.0) resolves it explicitly.
Template Specialisation — Providing a custom implementation for a specific type or set of types: template<> void print<bool>(bool b) overrides the generic print<T> for bool only. Full specialisation handles a single combination; partial specialisation handles a pattern (works for class templates).
Variadic Template (C++11) — A template that takes a variable number of arguments. template<typename... Args> declares a parameter pack. Used to implement std::make_unique, printf-style formatting (std::format), tuple, etc.
Parameter Pack / Pack Expansion — A template-parameter pack typename... Args can be expanded using Args... syntax to apply each argument in turn. The mechanism behind variadic templates.
STL (Standard Template Library) — The C++ standard library's collection of template-based containers, algorithms, and iterators: vector, list, map, set, sort, find, accumulate, etc. Designed by Alexander Stepanov; merged into the C++ standard in 1998.
Container (STL) — A template class that holds a collection of elements — vector<T>, list<T>, map<K, V>, set<T>, unordered_map, etc. Containers manage memory automatically (RAII).
Iterator (STL) — A template-based "pointer abstraction" over a container. Iterators support ++ (advance), * (dereference), == (compare). Algorithms operate on iterator ranges, decoupling them from the container type.
Algorithm (STL) — A template function that operates on iterator ranges — std::sort, std::find, std::accumulate, std::transform. Algorithms know nothing about the container; they just consume iterators. The genius of the STL: container × algorithm decoupling.
Concept (C++20) — A compile-time predicate on types, used to constrain template parameters. template<std::integral T> requires T to satisfy the integral concept. Concepts make template errors clean, document intent, and replace SFINAE in many cases.
SFINAE (Substitution Failure Is Not An Error) — A C++ idiom: when template type substitution fails, the compiler silently removes the candidate from overload resolution rather than emitting an error. Used heavily pre-C++20 for type-based dispatching; largely replaced by concepts in modern C++.
Template Metaprogramming (TMP) — Using templates to perform computation at compile time — compute factorials in types, generate code based on type traits, optimise away abstractions. TMP is Turing-complete; used in performance-critical libraries (Boost, Eigen).
Type Trait — A template-based compile-time query about a type — std::is_integral<T>, std::is_pointer<T>, std::is_base_of<Base, Derived>. Used to enable/disable templates based on type characteristics. Standardised in <type_traits>.
auto Keyword (C++11) — Lets the compiler deduce a variable's type from its initialiser. auto v = vector<int>{1, 2, 3};. Reduces verbosity in template-heavy code where types can be long. auto is type deduction at the variable level; templates are type deduction at the function/class level.
Lambda Expression (C++11) — An inline anonymous function with optional capture: auto add = [](int a, int b) { return a + b; };. Lambdas are conceptually functors generated by the compiler. C++14 added generic lambdas with auto parameters.
Zero-Overhead Abstraction — A C++ principle (Stroustrup): abstractions should cost nothing at runtime beyond what manually-written code would. Templates exemplify this — std::vector<int> is as fast as a hand-written int-array class.
Header-Only Library — A library whose entire implementation lives in header files, with no separate .cpp compilation. Template code typically must be in headers (the compiler needs to see definitions to instantiate). Most modern C++ libraries (Eigen, fmt, Catch2) are header-only.
ODR-Use of Template — A template entity that is referenced in a way that requires a definition. Triggers implicit instantiation. The C++ One Definition Rule is relaxed for templates: multiple identical instantiations are allowed across translation units.
Template Argument vs Template Parameter — Parameter is the placeholder in the declaration (T in template<typename T>). Argument is the actual type or value supplied at the use site (int in vector<int>).
Non-Type Template Parameter — A template parameter that holds a value, not a type — template<size_t N> class Array; with Array<5>. The value must be a constant expression known at compile time.
---
PYQ pattern (very common): "What is a function template? Explain with example." — Define generic programming; showtemplate<typename T>syntax; example withmaxorswap; mention compile-time instantiation.
PYQ pattern: "What is a class template? Write a Stack class as a template." — Define; show Stack<T> with push/pop; instantiate with int and string.
PYQ pattern: "Differentiate function template and function overloading." — Table: overloading has separate code per type; template has one generic; both compile-time; templates reduce duplication.