Function Templates — Type Deduction & explicit Instantiation
A **function template** is a blueprint for generating functions — the compiler substitutes the actual type(s) when the function is called. Template argument deduction automatically determines the type from the arguments. You can also **explicitly instantiate** to force a specific type. Templates are expanded at compile time — there is **zero runtime overhead**, but they increase compilation time and binary size if many types are instantiated.
Function Templates — Deduction, typename vs class & Concepts
#include <iostream>
#include <type_traits> // std::is_integral, etc.
#include <string>
#include <algorithm>
// ── Basic function template ───────────────────────────────────
// 'typename T' and 'class T' are identical in template declarations
template<typename T>
T max_of(T a, T b) {
return a > b ? a : b;
}
// ── Multiple type parameters ──────────────────────────────────
template<typename T, typename U>
auto add_hetero(T a, U b) -> decltype(a + b) {
// return type: whatever T+U yields (C++11 trailing return type)
return a + b;
}
// ── Non-type template parameters ─────────────────────────────
template<typename T, int N>
T sum_n(T (&arr)[N]) { // reference to array of N Ts — deduces N
T result{};
for (int i = 0; i < N; ++i) result += arr[i];
return result;
}
// ── Template with default argument ───────────────────────────
template<typename T, typename Compare = std::less<T>>
T find_min(const T* arr, int n, Compare cmp = Compare{}) {
T result = arr[0];
for (int i = 1; i < n; ++i)
if (cmp(arr[i], result)) result = arr[i];
return result;
}
// ── Constrained template (C++17 style with enable_if) ─────────
template<typename T,
typename = std::enable_if_t<std::is_arithmetic_v<T>>>
T square(T x) { return x * x; }
// square("hello") would fail — string is not arithmetic
// ── Explicit instantiation ────────────────────────────────────
// Forces compiler to generate the specialisation now (useful in .cpp files)
template int max_of<int>(int, int); // explicit instantiation
template double max_of<double>(double, double);
// ── Template function vs overloaded function priority ─────────
void process(int x) { std::cout << "overload: " << x << "
"; }
template<typename T> void process(T x) { std::cout << "template: " << x << "
"; }
int main() {
// Type deduction — compiler infers T=int
std::cout << max_of(3, 7) << "
"; // int → 7
std::cout << max_of(3.14, 2.71) << "
"; // double → 3.14
std::cout << max_of('a', 'z') << "
"; // char → z
// Explicit type argument — can disambiguate
std::cout << max_of<double>(3, 7.5) << "
"; // 7.5
// Heterogeneous add
auto r = add_hetero(3, 3.14); // int + double → double
std::cout << r << "
"; // 6.14
// Array reference — N deduced
int nums[] = {1, 2, 3, 4, 5};
std::cout << "sum=" << sum_n(nums) << "
"; // 15
// find_min with custom comparator
double vals[] = {3.1, 1.4, 2.7};
std::cout << "min=" << find_min(vals, 3) << "
"; // 1.4
// square — only compiles for arithmetic types
std::cout << square(5) << "
"; // 25
std::cout << square(3.14) << "
"; // 9.8596
// Overloaded function takes priority over template for exact match
process(42); // overload: 42 (exact match beats template)
process(3.14); // template: 3.14 (no overload for double)
process("hello"); // template: hello
return 0;
}Common Mistakes
- Template definition must be in the header (or same translation unit) — the compiler needs the full definition when instantiating at the call site. Putting template definitions in .cpp and declarations in .h gives 'undefined reference' linker errors for every instantiation.
- `max_of(3, 3.14)` fails to deduce — `T` would need to be both `int` and `double` simultaneously. Use `max_of<double>(3, 3.14)` or use the `auto` return type with two separate template parameters.
- Overload resolution: non-template exact matches beat templates — if you provide `void f(int)` and `template<T> void f(T)`, calling `f(42)` calls the non-template version. This is by design, but can surprise when debugging.
Tip
Tip
Practice Function Templates Type Deduction explicit Instantiation in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
The four pillars of Object-Oriented Programming in C++
Practice Task
Note
Practice Task — (1) Write a working example of Function Templates Type Deduction explicit Instantiation from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Function Templates Type Deduction explicit Instantiation is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready cpp code.
Key Takeaways
- A **function template** is a blueprint for generating functions — the compiler substitutes the actual type(s) when the function is called.
- Template definition must be in the header (or same translation unit) — the compiler needs the full definition when instantiating at the call site. Putting template definitions in .cpp and declarations in .h gives 'undefined reference' linker errors for every instantiation.
- `max_of(3, 3.14)` fails to deduce — `T` would need to be both `int` and `double` simultaneously. Use `max_of<double>(3, 3.14)` or use the `auto` return type with two separate template parameters.
- Overload resolution: non-template exact matches beat templates — if you provide `void f(int)` and `template<T> void f(T)`, calling `f(42)` calls the non-template version. This is by design, but can surprise when debugging.