Practical examples of C++ templates: function & class examples

If you’re trying to move beyond copy‑pasting overloads and really use modern C++, you need to get comfortable with templates. The fastest way to do that is to walk through practical examples of C++ templates: function & class examples you’d actually write in real projects. Instead of abstract theory, we’ll focus on real examples you can drop into your own code. In this guide, we’ll start with simple function template examples, then grow into class templates, type traits, and even a taste of C++20 concepts. Along the way, you’ll see how examples of C++ templates: function & class examples show up in the standard library itself—`std::vector`, `std::sort`, and `std::optional` are all powered by templates under the hood. By the end, you’ll have a mental toolbox of patterns and real examples you can adapt, not just syntax rules to memorize.
Written by
Jamie
Published

Before worrying about theory, let’s look at a few real examples of C++ templates: function & class examples that mirror what you already do with plain functions and classes.

Here’s a simple function template that works for int, double, or any type that supports +:

// Function template: generic add

template <typename T>
T add(T a, T b) {
    return a + b;
}

int main() {
    int x = add(2, 3);          // T deduced as int
    double y = add(2.5, 3.1);   // T deduced as double
}

And here’s a minimal class template that looks a lot like a stripped‑down std::pair:

// Class template: simple Pair

template <typename T, typename U>
class Pair {
public:
    T first;
    U second;

    Pair(const T& f, const U& s) : first(f), second(s) {}
};

int main() {
    Pair<int, std::string> p(42, "answer");
}

Those two snippets already cover the core idea behind most examples of C++ templates: function & class examples are just blueprints that the compiler uses to generate concrete code for specific types.


Function template examples: from trivial to actually useful

Let’s stay with function templates for a bit, because they’re the easiest way to build intuition.

Example 1: type‑agnostic max function

The classic beginner example of a function template is a generic max:

#include <iostream>

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

int main() {
    std::cout << my_max(3, 7) << '\n';             // int
    std::cout << my_max(3.14, 2.71) << '\n';       // double
    std::cout << my_max('a', 'z') << '\n';         // char
}

This is one of the best examples of a function template because it mirrors the standard library’s own std::max. You write the logic once, and the compiler generates a version for every type you use.

Example 2: function template with two different types

Sometimes your function naturally handles two different types, such as adding an int to a double:

#include <type_traits>

// Add two possibly different types and return the common type

template <typename T, typename U>
auto add_mixed(T a, U b) {
    return a + b;   // return type deduced by auto
}

int main() {
    auto r1 = add_mixed(1, 2.5);        // double
    auto r2 = add_mixed(3u, 4LL);       // long long (implementation-dependent)
}

This pattern shows up constantly in numeric and graphics code, where mixing integer and floating‑point types is normal.

Example 3: function template with non‑type parameter (array size)

Non‑type template parameters are underrated. They let you treat values (like array sizes) as template parameters:

#include <cstddef>
#include <iostream>

// Print a fixed-size C-style array

template <typename T, std::size_t N>
void print_array(const T (&arr)[N]) {
    for (std::size_t i = 0; i < N; ++i) {
        std::cout << arr[i] << (i + 1 < N ? ' ' : '\n');
    }
}

int main() {
    int nums[] = {1, 2, 3, 4};
    print_array(nums);   // N deduced as 4
}

This is a concrete example of a function template that avoids the classic bug of passing the wrong array size.

Example 4: C++20 constrained function template (concepts)

Modern C++ (C++20 and later) adds concepts, which let you constrain templates in a readable way:

#include <concepts>

// Only accept arithmetic types

template <std::integral T>
T gcd(T a, T b) {
    while (b != 0) {
        T t = b;
        b = a % b;
        a = t;
    }
    return a;
}

int main() {
    auto g = gcd(48, 18);   // OK
    // auto bad = gcd(4.5, 1.5); // error: not integral
}

This is a modern example of C++ templates: function & class examples using language features that are standard as of 2024 and widely supported in compilers like GCC, Clang, and MSVC.

For the C++20 standard text and concepts reference, see the ISO C++ committee’s public drafts hosted via cppreference.com.


Class template examples: containers, wrappers, and policies

Function templates are great, but class templates are where C++ really leans into generic programming. Many of the best examples of C++ templates: function & class examples live in the standard library itself: std::vector<T>, std::map<Key, T>, std::optional<T>, and more.

Example 5: a minimal Vector2 math type

Game and graphics engines nearly always define template vector types:

template <typename T>
class Vector2 {
public:
    T x{};
    T y{};

    Vector2() = default;
    Vector2(T x_, T y_) : x(x_), y(y_) {}

    Vector2 operator+(const Vector2& other) const {
        return {x + other.x, y + other.y};
    }
};

int main() {
    Vector2<float> pos(1.0f, 2.0f);
    Vector2<float> vel(0.5f, 0.0f);
    auto next = pos + vel;
}

This single class template gives you Vector2<float>, Vector2<double>, or even Vector2<int> without repeating code.

Example 6: type‑safe ID wrapper

A very practical example of a class template is a strong typedef or ID wrapper:

#include <cstdint>

// Distinguish between different ID types at compile time

template <typename Tag>
class Id {
public:
    using value_type = std::uint32_t;

    explicit Id(value_type v = 0) : value_(v) {}

    value_type value() const { return value_; }

    friend bool operator==(Id a, Id b) { return a.value_ == b.value_; }

private:
    value_type value_;
};

struct UserTag {};
struct OrderTag {};

using UserId  = Id<UserTag>;
using OrderId = Id<OrderTag>;

int main() {
    UserId u(1);
    OrderId o(1);
    // u == o; // compile-time error: different types
}

This pattern shows how examples of C++ templates: function & class examples can actually prevent bugs by making invalid comparisons impossible.

Example 7: small fixed‑size buffer with non‑type parameter

Here’s a class template that uses a non‑type template parameter to control capacity at compile time:

#include <cstddef>
#include <stdexcept>

// Simple fixed-size ring buffer

template <typename T, std::size_t Capacity>
class RingBuffer {
public:
    bool push(const T& value) {
        if (size_ == Capacity) return false;
        data_[(head_ + size_) % Capacity] = value;
        ++size_;
        return true;
    }

    T pop() {
        if (size_ == 0) throw std::out_of_range("empty");
        T value = data_[head_];
        head_ = (head_ + 1) % Capacity;

        --size_;
        return value;
    }

    std::size_t size() const { return size_; }

private:
    T data_[Capacity]{};
    std::size_t head_ = 0;
    std::size_t size_ = 0;
};

int main() {
    RingBuffer<int, 8> rb;  // capacity known at compile time
}

Embedded systems and high‑performance trading codebases are full of real examples like this, where compile‑time sizes matter for performance and predictability.

Example 8: policy‑based logging class template

Templates also shine when you want to inject behavior, not just types. A common pattern is a logger that takes a policy class as a template parameter:

#include <iostream>
#include <string>

struct ConsolePolicy {
    void write(const std::string& msg) {
        std::cout << msg << '\n';
    }
};

struct NullPolicy {
    void write(const std::string&) {
        // no-op
    }
};

// Logger template parameterized by policy type

template <typename Policy>
class Logger {
public:
    void log(const std::string& msg) {
        policy_.write(msg);
    }

private:
    Policy policy_{};
};

int main() {
    Logger<ConsolePolicy> console_logger;
    Logger<NullPolicy>    disabled_logger;

    console_logger.log("Hello, templates!");
    disabled_logger.log("This goes nowhere");
}

This is one of the best examples of C++ templates: function & class examples where templates replace inheritance for customization.


Template specialization and partial specialization examples

Sometimes you want a generic template and a special case for a specific type. That’s where specialization comes in.

Example 9: full specialization for bool

Imagine a simple storage wrapper where bool gets a different representation:

// Primary template

template <typename T>
class Storage {
public:
    void set(const T& value) { value_ = value; }
    const T& get() const { return value_; }
private:
    T value_{};
};

// Full specialization for bool

template <>
class Storage<bool> {
public:
    void set(bool value) { value_ = value; }
    bool get() const { return value_; }
private:
    unsigned char value_ : 1; // store as single bit
};

int main() {
    Storage<int>  si;
    Storage<bool> sb;
}

This mirrors what std::vector<bool> famously does (for better or worse) in the standard library.

Example 10: partial specialization for pointer types

Partial specialization lets you tweak behavior for a family of types, such as all pointers:

#include <cstddef>

// Primary template

template <typename T>
struct TypeInfo {
    static constexpr bool is_pointer = false;
};

// Partial specialization for pointer types

template <typename T>
struct TypeInfo<T*> {
    static constexpr bool is_pointer = true;
};

int main() {
    static_assert(!TypeInfo<int>::is_pointer);
    static_assert(TypeInfo<int*>::is_pointer);
}

This is a simple example of how the standard library builds type traits like std::is_pointer and std::is_integral.

For a deeper reference on specialization and traits, the C++ Core Guidelines maintained by the ISO C++ community and Microsoft are a good starting point: C++ Core Guidelines.


How the standard library uses templates (and why you should care)

If you’re looking for real examples of C++ templates: function & class examples in production‑grade code, you don’t have to look further than the standard library.

Some everyday tools that are themselves templates:

  • std::vector<T> – dynamic array for any T
  • std::array<T, N> – fixed‑size array with compile‑time length
  • std::optional<T> – maybe‑present value, heavily used in modern C++ APIs
  • std::function<R(Args...)> – type‑erased callable wrapper
  • std::unique_ptr<T> / std::shared_ptr<T> – smart pointers managing ownership

Each of these is a polished example of C++ templates: function & class examples that have been battle‑tested in real software. Reading how they’re specified (see cppreference.com) is a surprisingly good way to pick up patterns and idioms.

Industry surveys, including the JetBrains “C++ Ecosystem” reports for 2023–2024, show that a large majority of C++ developers target C++17 or later. That means template‑heavy features like std::optional, std::variant, and std::filesystem are now mainstream, not exotic.


Modern tips for working with template heavy code (2024–2025)

As of 2024–2025, three trends dominate real examples of C++ templates: function & class examples in new codebases:

  • Use auto generously for return types and local variables when the exact type isn’t part of your API contract. It keeps template code readable.
  • Adopt concepts to make error messages sane. A constrained template with std::integral or a custom concept is far easier to debug than a 50‑line substitution failure.
  • Lean on standard type traits like std::is_same_v, std::enable_if_t, and std::conditional_t instead of rolling your own metaprogramming from scratch.

For structured learning, many universities host modern C++ material; for example, MIT’s OpenCourseWare and similar platforms often include C++ template content alongside systems programming courses: MIT OpenCourseWare.


FAQ: short answers with more examples

What are some simple examples of C++ templates I can memorize?

Two minimal patterns worth memorizing are:

// Function template
template <typename T>
T square(T x) { return x * x; }

// Class template
template <typename T>
class Box {
public:
    explicit Box(const T& value) : value_(value) {}
    const T& get() const { return value_; }
private:
    T value_;
};

These examples of C++ templates: function & class examples map almost directly to how std::optional<T> or std::vector<T> are written.

Can you show an example of mixing templates and lambdas?

Yes. Lambdas themselves are templates under the hood, but you can also write a function template that takes a lambda:

#include <vector>

template <typename Container, typename Func>
void for_each_indexed(Container& c, Func f) {
    std::size_t i = 0;
    for (auto& elem : c) {
        f(i++, elem);
    }
}

int main() {
    std::vector<int> v{1, 2, 3};
    for_each_indexed(v, [](std::size_t i, int& x) {
        x += static_cast<int>(i);
    });
}

This is a practical example of a function template that works with any container and any callable.

Where can I see more real examples of C++ templates: function & class examples?

Good places to study real‑world patterns:

Reading and imitating these sources will give you more grounded examples of C++ templates: function & class examples than any toy snippet can.

Explore More C++ Code Snippets

Discover more examples and insights in this category.

View All C++ Code Snippets