READMEC++

README

Templates / Function Templates

Concept Lesson
Intermediate
4 min

Learning Objective

Understand Templates well enough to explain it, recognize it in C++, and apply it in a small task.

Why It Matters

This concept is part of the foundation that later lessons and projects assume you already understand.

Table Of ContentsWhat Are TemplatesThe Problem Templates SolveHow Template Instantiation WorksInstantiation Process Visualization
Private notes
0/8000

Notes stay private to your browser until account sync is configured.

README
1 min read18 headings

Function Templates in C++

Table of Contents

  1. What are Templates
  2. How Template Instantiation Works
  3. Basic Syntax
  4. Type Deduction
  5. Multiple Type Parameters
  6. Non-Type Parameters
  7. Template Specialization
  8. Best Practices

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

AspectDescription
WhenAt compile time, when template is used with a specific type
WhereOnly for types actually used in your code
Code Bloat?Each instantiation creates new code, but only for used types
Header FilesTemplates 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");
}

Best Practices

✅ 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

Quick Reference

// 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

Skill Check

Test this lesson

Answer 4 quick questions to lock in the lesson and feed your adaptive practice queue.

--
Score
0/4
Answered
Not attempted
Status
1

Which module does this lesson belong to?

2

Which section is covered in this lesson content?

3

Which term is most central to this lesson?

4

What is the best way to use this lesson for real learning?

Your answers save locally first, then sync when account storage is available.
Practice queue