Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

Templates are the foundation of generic programming in C++. Because C++ is strongly typed, every variable and function argument must have a concrete type — but many algorithms and data structures behave identically regardless of the types they operate on. Templates let you write code once and have the compiler generate type-specific versions automatically at compile time. The process of generating a concrete class or function from a template is called template instantiation. This page covers function templates, class templates, type and non-type parameters, default arguments, full and partial specialization, variadic templates, and C++20 concepts.

Function Templates

A function template defines a function in terms of one or more type parameters. When the function is called, the compiler deduces the type arguments from the call arguments and generates a concrete function:
template <typename T>
T minimum(const T& lhs, const T& rhs)
{
    return lhs < rhs ? lhs : rhs;
}

int a = 3, b = 7;
int i = minimum<int>(a, b);   // explicit type argument: minimum<int>
int j = minimum(a, b);         // type deduced: T = int

double x = 1.5, y = 2.5;
double d = minimum(x, y);      // T = double
The keyword typename (or equivalently class in this context) introduces the type parameter T. When the compiler instantiates minimum<int>, it generates:
int minimum(const int& lhs, const int& rhs)
{
    return lhs < rhs ? lhs : rhs;
}
Type arguments must support every operation the template applies to them. If you call minimum with a type that has no < operator, the compiler generates an error at the point of instantiation. C++20 concepts provide a cleaner way to express these requirements — see the Concepts section below.

Class Templates

Class templates generate class types at compile time. The C++ Standard Library’s containers (std::vector, std::map, etc.) are all class templates:
template <typename T, size_t L>
class MyArray
{
    T arr[L];
public:
    MyArray() { std::fill(arr, arr + L, T{}); }

    T&       operator[](size_t i)       { return arr[i]; }
    const T& operator[](size_t i) const { return arr[i]; }
    size_t   size() const { return L; }
};

MyArray<int, 10>    ints;
MyArray<double, 4>  doubles;
ints[0] = 42;

Multiple Type Parameters

template <typename K, typename V>
class Pair {
public:
    Pair(K key, V value) : m_key(key), m_value(value) {}
    K key()   const { return m_key; }
    V value() const { return m_value; }
private:
    K m_key;
    V m_value;
};

Pair<std::string, int> entry("score", 100);

Type Parameters

There is no practical limit on the number of type parameters. Separate multiple parameters with commas. Both typename and class are interchangeable in template parameter lists:
template <typename T, typename U, typename V>
class Foo {};

template <class T, class U, class V>
class Bar {};  // identical to above

// Using std::vector with various types
std::vector<int>         vi;
std::vector<std::string> vs;
std::vector<MyClass*>    vp;

Non-Type Parameters

Unlike generics in Java or C#, C++ templates support non-type parameters — compile-time constant values such as integers, pointers, or enumerators:
template <typename T, size_t L>
class StaticArray {
    T data[L];
public:
    constexpr size_t size() const { return L; }
    T& operator[](size_t i) { return data[i]; }
};

StaticArray<float, 4>  vec4;      // 4-element float array on the stack
StaticArray<int,   10> intBuf;    // 10-element int array

// C++17: non-type template parameter with auto
template <auto x>
constexpr auto constant = x;

auto v1 = constant<5>;      // v1 == 5,    decltype(v1) is int
auto v2 = constant<true>;   // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>;    // v3 == 'a',  decltype(v3) is char

Default Template Arguments

Template parameters can have default arguments, just like function parameters:
// std::vector uses a default allocator argument
template <class T, class Allocator = std::allocator<T>>
class vector { /* ... */ };

// Most code doesn't specify an allocator:
std::vector<int> nums;                          // uses std::allocator<int>
std::vector<int, MyAllocator<int>> custom;      // custom allocator

// Class template with all defaults — use empty angle brackets
template <typename A = int, typename B = double>
class Pair {
    A first;
    B second;
};

Pair<>              p1;           // Pair<int, double>
Pair<float>         p2;           // Pair<float, double>
Pair<float, float>  p3;           // Pair<float, float>

Template Specialization

Sometimes the generic template code is not correct or optimal for a specific type. Template specialization lets you provide a hand-written version for particular type arguments.

Full (Explicit) Specialization

// Primary template
template <typename T>
class Serializer {
public:
    static std::string serialize(const T& val) {
        return std::to_string(val);
    }
};

// Full specialization for bool
template <>
class Serializer<bool> {
public:
    static std::string serialize(bool val) {
        return val ? "true" : "false";
    }
};

Serializer<int>::serialize(42);    // "42"
Serializer<bool>::serialize(true); // "true"

Partial Specialization

Partial specialization applies when only some of the template parameters are fixed:
template <typename K, typename V>
class MyMap { /* generic */ };

// Partial specialization for string keys
template <typename V>
class MyMap<std::string, V> {
    // optimized implementation for string keys
};

MyMap<int, double>          m1;   // uses primary template
MyMap<std::string, double>  m2;   // uses partial specialization
Only class templates (not function templates) can be partially specialized. For function templates, use overloading or if constexpr (C++17) instead of partial specialization.

Variadic Templates

Variadic templates accept an arbitrary number of type arguments using the ellipsis operator ...:
// A simple type-safe print function using variadic templates
template <typename T>
void print(T value) {
    std::cout << value << "\n";
}

template <typename T, typename... Args>
void print(T first, Args... rest) {
    std::cout << first << " ";
    print(rest...);   // recursive instantiation
}

print(1, 2.5, "hello", true);
// Output: 1 2.5 hello 1

// Forward all arguments to another function
template <typename... Args>
void log_and_call(void(*fn)(Args...), Args... args) {
    std::cout << "Calling function\n";
    fn(args...);
}
Variadic templates underpin std::tuple, std::variant, std::make_unique, and many other Standard Library types.

Templates as Template Parameters

A template can itself be a template parameter:
template <typename T, template <typename U, int I> class Arr>
class MyClass2 {
    T t;
    Arr<T, 10> a;
};

if constexpr for Compile-Time Branching (C++17)

if constexpr selects branches at compile time inside function templates, eliminating the need for many specializations:
template <typename T>
auto describe(T val) {
    if constexpr (std::is_integral_v<T>) {
        return std::string("integer: ") + std::to_string(val);
    } else if constexpr (std::is_floating_point_v<T>) {
        return std::string("float: ") + std::to_string(val);
    } else {
        return std::string("other type");
    }
}

describe(42);    // "integer: 42"
describe(3.14);  // "float: 3.140000"

Concepts (C++20)

Concepts are named compile-time predicates that constrain template parameters. They replace the cryptic error messages that result from constraint violations with clear, readable diagnostics:
#include <concepts>

// Define a concept
template <typename T>
concept Arithmetic = std::is_arithmetic_v<T>;

// Constrain a function template with a concept
template <Arithmetic T>
T add(T a, T b) {
    return a + b;
}

add(1, 2);          // OK: int satisfies Arithmetic
add(1.5, 2.5);      // OK: double satisfies Arithmetic
// add("a", "b");   // Error: const char* does not satisfy Arithmetic

// Concept with requires clause
template <typename T>
concept Comparable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
};

template <Comparable T>
T my_min(T a, T b) { return a < b ? a : b; }
Prefer standard library concepts from <concepts> (such as std::integral, std::floating_point, std::same_as, std::convertible_to) before writing your own. They are well-tested and compose naturally.

Practical Example: Type-Safe Stack

template <typename T, size_t Capacity = 64>
class Stack {
public:
    void push(const T& item) {
        if (m_size >= Capacity)
            throw std::overflow_error("Stack full");
        m_data[m_size++] = item;
    }

    T pop() {
        if (m_size == 0)
            throw std::underflow_error("Stack empty");
        return m_data[--m_size];
    }

    const T& top() const { return m_data[m_size - 1]; }
    bool empty() const   { return m_size == 0; }
    size_t size() const  { return m_size; }

private:
    T      m_data[Capacity];
    size_t m_size = 0;
};

Stack<int>      istack;
Stack<double, 32> dstack;
istack.push(1);
istack.push(2);
std::cout << istack.pop();  // 2

See Also

Build docs developers (and LLMs) love