Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Generic Programming

Templates and Generic Programming

Templates: Compile-Time Polymorphism

Templates enable writing code that works with any type satisfying certain requirements, with full type checking at compile time and zero runtime overhead. Unlike runtime polymorphism (virtual functions), templates generate specialized code for each type used.

Function Templates

template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

auto x = max(10, 20);         // T = int
auto y = max(3.14, 2.71);     // T = double
auto z = max<long>(5, 10);    // Explicit T = long

Template Argument Deduction

The compiler deduces template parameters from function arguments. Deduction follows strict rules:

template<typename T>
void process(T value, T* ptr);

int x = 5;
process(x, &x);  // OK: T = int

double d = 3.14;
// process(x, &d);  // Error: T is int or double?

Class Templates

template<typename T>
class Stack {
    std::vector<T> data_;
public:
    void push(const T& value) { data_.push_back(value); }
    
    T pop() {
        T value = data_.back();
        data_.pop_back();
        return value;
    }
    
    bool empty() const { return data_.empty(); }
};

Stack<int> intStack;
Stack<std::string> stringStack;

Member Function Templates

Templates can appear at multiple levels:

template<typename T>
class Container {
public:
    // Member function template
    template<typename U>
    void insert(U&& value) {
        // ...
    }
};

Container<int> c;
c.insert(42);       // U = int
c.insert("hello");  // U = const char*

Template Specialization

Full Specialization

Provide a completely different implementation for a specific type:

template<typename T>
class TypeName {
public:
    static const char* get() { return "unknown"; }
};

template<>
class TypeName<int> {
public:
    static const char* get() { return "int"; }
};

template<>
class TypeName<double> {
public:
    static const char* get() { return "double"; }
};

Partial Specialization (Class Templates Only)

Specialize for a subset of template parameters:

template<typename T, typename U>
class Pair {
    T first_;
    U second_;
};

// Partial specialization: both types the same
template<typename T>
class Pair<T, T> {
    T first_, second_;
public:
    bool equal() const { return first_ == second_; }
};

// Partial specialization: pointer types
template<typename T>
class Pair<T*, T*> {
    // Special implementation for pointers
};

Non-Type Template Parameters

Templates can take compile-time constant values:

template<typename T, size_t N>
class Array {
    T data_[N];
public:
    constexpr size_t size() const { return N; }
    
    T& operator[](size_t i) { return data_[i]; }
    const T& operator[](size_t i) const { return data_[i]; }
};

Array<int, 10> arr1;      // Different type from...
Array<int, 20> arr2;      // ...this

Non-type parameters can be: integers, enums, pointers, references, nullptr_t, and (C++20) floating-point and class types.

Variadic Templates (C++11)

Templates accepting arbitrary numbers of arguments:

template<typename... Args>
void print(Args... args) {
    ((std::cout << args << ' '), ...);  // Fold expression (C++17)
    std::cout << '\\n';
}

print(1, 2.5, "hello", 'x');  // Any number/types of arguments

Parameter Pack Expansion

template<typename... Ts>
class Tuple;  // Declaration

// Recursive inheritance
template<typename T, typename... Ts>
class Tuple<T, Ts...> : public Tuple<Ts...> {
    T value_;
public:
    Tuple(T v, Ts... vs) : Tuple<Ts...>(vs...), value_(v) {}
};

template<>
class Tuple<> {  // Base case
};

SFINAE: Substitution Failure Is Not An Error

When template substitution fails, the candidate is removed from overload set instead of causing error:

template<typename T>
typename T::value_type getValue(T container) {  // Only if T has value_type
    return container[0];
}

template<typename T>
T getValue(T value) {  // Fallback
    return value;
}

std::vector<int> vec{1, 2, 3};
auto x = getValue(vec);  // Calls first overload
auto y = getValue(42);   // Calls second overload (first SFINAE'd out)

Modern C++ uses std::enable_if for SFINAE:

template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
increment(T value) {
    return value + 1;
}

increment(10);    // OK
// increment(3.14);  // Error: no matching function

C++20 concepts provide cleaner syntax:

template<std::integral T>
T increment(T value) {
    return value + 1;
}
Conceptual Check

What happens when you instantiate Array<int, 5> and Array<int, 10>?

Runtime Environment

Interactive Lab

1#include <iostream>
2 
3template<typename T>
4T add(T a, T b) {
5 return a + b;
6}
7 
8template<typename T, size_t N>
9class FixedArray {
10 T data_[N];
11public:
12 constexpr size_t size() const { return N; }
13};
14 
15int main() {
16 std::cout << add(5, 10) << '\n';
17 std::cout << add(1.5, 2.5) << '\n';
18
19 FixedArray<int, 5> arr;
20 std::cout << "Array size: " << arr.size() << '\n';
21 return 0;
22}
System Console

Waiting for signal...