Templates
Function Templates in C++
Table of Contents
What are Templates
Templates enable generic programming - write code once, use with any type. The compiler generates specific versions for each type you use.
The Problem Templates Solve
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WITHOUT TEMPLATES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β You need a max function for different types... β
β β
β int maxInt(int a, int b) { return a > b ? a : b; } β
β double maxDouble(double a, double b) { return a > b ? a : b; } β
β float maxFloat(float a, float b) { return a > b ? a : b; } β
β long maxLong(long a, long b) { return a > b ? a : b; } β
β string maxString(string a, string b) { return a > b ? a : b; } β
β β
β β Repetitive code! β
β β Easy to introduce bugs β
β β Must add new function for each type β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β WITH TEMPLATES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β template<typename T> β
β T max(T a, T b) { return a > b ? a : b; } β
β β
β max(1, 2); // Works with int β
β max(1.5, 2.5); // Works with double β
β max('a', 'z'); // Works with char β
β max(str1, str2); // Works with string β
β β
β β
Write once, works with ANY type that supports > β
β β
Type-safe (compiler catches mismatches) β
β β
No runtime overhead (resolved at compile time) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
How Template Instantiation Works
When you use a template with a specific type, the compiler generates actual code for that type. This is called template instantiation.
Instantiation Process Visualization
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TEMPLATE INSTANTIATION PROCESS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β SOURCE CODE (what you write): β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β template<typename T> β β
β β T square(T x) { return x * x; } β β
β β β β
β β int main() { β β
β β square(5); // Use with int β β
β β square(3.14); // Use with double β β
β β square(2.5f); // Use with float β β
β β } β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β COMPILER SEES USAGES β
β and generates code: β
β β β
β βββββββββββββββββββββββββββΌββββββββββββββββββββββββββ β
β β β β β
β βΌ βΌ βΌ β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β T = int β β T = double β β T = float β β
β β β β β β β β
β β int square β βdouble square β βfloat square β β
β β (int x) { β β(double x) { β β(float x) { β β
β β return x*x;β β return x*x; β β return x*x; β β
β β } β β} β β} β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β
β COMPILED BINARY contains ALL THREE versions β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Points About Instantiation
| Aspect | Description |
|---|---|
| When | At compile time, when template is used with a specific type |
| Where | Only for types actually used in your code |
| Code Bloat? | Each instantiation creates new code, but only for used types |
| Header Files | Templates must be defined in headers (not .cpp files) |
Basic Syntax
template<typename T> // or template<class T>
T functionName(T param) {
// T is the placeholder type
return param;
}
Examples
template<typename T>
T square(T x) {
return x * x;
}
template<typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
template<typename T>
void print(const vector<T>& vec) {
for (const T& item : vec) {
cout << item << " ";
}
}
Type Deduction
Compiler deduces T from arguments:
template<typename T>
T add(T a, T b) { return a + b; }
add(1, 2); // T deduced as int
add(1.5, 2.5); // T deduced as double
// Explicit specification
add<long>(1, 2); // Force T = long
Deduction Conflicts
template<typename T>
T add(T a, T b) { return a + b; }
// add(1, 2.5); // ERROR: T = int or double?
// Solutions:
add<double>(1, 2.5); // Explicit
add(1.0, 2.5); // Make types match
Multiple Type Parameters
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
// C++14 simplified
template<typename T, typename U>
auto add(T a, U b) {
return a + b;
}
add(1, 2.5); // int + double = double
add(1.5, 2); // double + int = double
Return Type Issues
template<typename T, typename U>
T badAdd(T a, U b) { return a + b; } // Truncates if U > T
template<typename T, typename U>
auto goodAdd(T a, U b) { return a + b; } // Correct return type
Non-Type Parameters
template<typename T, int Size>
class Array {
T data[Size];
public:
int size() const { return Size; }
};
Array<int, 10> arr; // Fixed size at compile time
// Function with non-type parameter
template<int N>
int multiply(int x) {
return x * N;
}
multiply<5>(10); // Returns 50
Template Specialization
Override template for specific types:
// Primary template
template<typename T>
T absolute(T x) {
return x < 0 ? -x : x;
}
// Specialization for bool
template<>
bool absolute<bool>(bool x) {
return x; // No negation for bool
}
// Partial specialization (for pointers)
template<typename T>
T* absolute(T* ptr) {
return ptr; // Just return pointer
}
Why Specialize?
// Generic print
template<typename T>
void print(const T& value) {
cout << value;
}
// Specialize for C-strings
template<>
void print<const char*>(const char* const& value) {
cout << "\"" << value << "\"";
}
// Specialize for bool
template<>
void print<bool>(const bool& value) {
cout << (value ? "true" : "false");
}
Recommended Conventions
β Do
// 1. Use auto for return types (C++14)
template<typename T, typename U>
auto add(T a, U b) { return a + b; }
// 2. Use const& for non-modified parameters
template<typename T>
void process(const T& item);
// 3. Use forwarding references for perfect forwarding
template<typename T>
void forward(T&& arg);
// 4. Define in headers
// Templates must be visible at instantiation
β Don't
// 1. Don't assume operators exist
template<typename T>
T multiply(T a, T b) { return a * b; } // T must support *
// 2. Don't separate declaration/definition
// template.h - declaration only - WON'T WORK
// template.cpp - definition - WON'T LINK
Cheat Sheet
// Basic template
template<typename T>
T func(T param);
// Multiple types
template<typename T, typename U>
auto func(T a, U b);
// Non-type parameter
template<typename T, int N>
void func(T (&arr)[N]);
// Specialization
template<>
void func<int>(int param);
// Default template argument
template<typename T = int>
T func();
Compile & Run
g++ -std=c++17 -Wall examples.cpp -o examples && ./examples