What Are Class Templates?

A class template is a blueprint for creating struct or class definitions where one or more types are parameterized, allowing the compiler to generate specialized versions for each type you need rather than writing duplicate code by hand.

Suppose you're building a game inventory system that tracks item quantities:

struct IntSlot
{
    int item{};
    int quantity{};
};

This works for items identified by integer IDs. But your UI needs string-based item names:

struct StringSlot
{
    std::string item{};
    int quantity{};
};

Now you have two nearly identical structs. Adding support for item enums means a third. The pattern repeats: same structure, different types.

Class templates solve this

A class template is a blueprint for generating struct and class definitions. You define the pattern once, and the compiler creates specialized versions for each type you need.

template <typename T>
struct Slot
{
    T item{};
    int quantity{};
};

Now you can create slots holding any item type:

Slot<int> byId{ 42, 5 };             // item is int
Slot<std::string> byName{ "sword", 1 }; // item is std::string

Defining a class template

Start with the template parameter declaration, then define the struct using the placeholder type:

template <typename T>
struct Container
{
    T value{};
    bool hasValue{ false };
};

The syntax mirrors function templates:

  • template <typename T> declares the placeholder type
  • T can be used wherever you need a flexible type
  • Members can mix template and concrete types

Instantiating class templates

Specify the type in angle brackets when creating objects:

#include <iostream>
#include <string>

template <typename T>
struct Container
{
    T value{};
    bool hasValue{ false };
};

int main()
{
    Container<int> count{ 42, true };
    Container<double> measurement{ 98.6, true };
    Container<std::string> message{ "Hello", true };

    std::cout << count.value << '\n';        // 42
    std::cout << measurement.value << '\n';  // 98.6
    std::cout << message.value << '\n';      // Hello

    return 0;
}

The compiler generates three distinct types:

  • Container<int> with an int value member
  • Container<double> with a double value member
  • Container<std::string> with a std::string value member

What the compiler generates

When you write:

Container<int> count{ 42, true };

The compiler produces something equivalent to:

struct Container_int
{
    int value{};
    bool hasValue{ false };
};

Container_int count{ 42, true };

Each unique template argument creates one type definition. Container<int> and Container<double> are completely separate types that happen to share a structural pattern.

Using class templates with function templates

Class templates often pair with function templates:

#include <iostream>

template <typename T>
struct Range
{
    T low{};
    T high{};
};

template <typename T>
T midpoint(Range<T> r)
{
    return (r.low + r.high) / 2;
}

int main()
{
    Range<int> scores{ 0, 100 };
    Range<double> temperatures{ -40.0, 120.0 };

    std::cout << midpoint(scores) << '\n';       // 50
    std::cout << midpoint(temperatures) << '\n'; // 40

    return 0;
}

The function template's T matches the class template's T, allowing seamless type deduction.

Multiple template parameters

Class templates can have multiple type parameters:

template <typename K, typename V>
struct Entry
{
    K key{};
    V value{};
};

Usage:

Entry<int, std::string> userById{ 1001, "Alice" };
Entry<std::string, double> priceByName{ "apple", 1.99 };
Entry<char, int> asciiValue{ 'A', 65 };

Each parameter resolves independently, allowing different combinations.

Functions accepting multi-parameter templates

Function templates working with multi-parameter class templates need matching declarations:

#include <iostream>

template <typename K, typename V>
struct Entry
{
    K key{};
    V value{};
};

template <typename K, typename V>
void display(Entry<K, V> e)
{
    std::cout << e.key << " -> " << e.value << '\n';
}

int main()
{
    Entry<std::string, int> score{ "Player1", 2500 };
    display(score);  // Player1 -> 2500

    return 0;
}

Generic function parameters

Sometimes you want a function that works with any type having certain members:

template <typename T>
void showKeyValue(T item)
{
    std::cout << item.key << ": " << item.value << '\n';
}

This works with any type that has key and value members, not just Entry:

struct Config
{
    std::string key{};
    int value{};
};

Entry<std::string, double> setting{ "volume", 0.75 };
Config option{ "maxPlayers", 8 };

showKeyValue(setting);  // Works with Entry
showKeyValue(option);   // Works with Config too

The standard library's std::pair

The standard library provides std::pair<T, U> for paired values:

#include <utility>
#include <iostream>

int main()
{
    std::pair<std::string, int> player{ "Alice", 100 };

    std::cout << player.first << ": " << player.second << '\n';

    return 0;
}

Members are named first and second. Use std::pair instead of writing custom pair types.

Class templates in header files

Place class templates in headers for use across multiple files:

container.h:

#pragma once

template <typename T>
struct Container
{
    T value{};
    bool hasValue{ false };
};

template <typename T>
bool isEmpty(Container<T> c)
{
    return !c.hasValue;
}

main.cpp:

#include "container.h"
#include <iostream>

int main()
{
    Container<int> data{ 42, true };
    std::cout << isEmpty(data) << '\n';  // 0 (false)

    return 0;
}

Templates are exempt from the one-definition rule, so including them in multiple files works correctly.

Summary

Concept Syntax
Single-type template template <typename T> struct Name { T member; };
Multi-type template template <typename T, typename U> struct Name { T a; U b; };
Instantiation Name<int> obj{ value };
Function with template param template <typename T> void func(Name<T> param)

Key points:

  • Class templates generate type definitions from a single pattern
  • Each unique template argument set produces a distinct type
  • Template parameters can mix with concrete types in the same struct
  • Function templates naturally pair with class templates
  • std::pair<T, U> is the standard library's general-purpose pair type
  • Place templates in headers for multi-file usage