Class Templates & Specialization

ক্লাস টেমপ্লেট ও স্পেশালাইজেশন

Read: ~35 min 16 practice problems

1. A Generic Stack

stack.cpp
#include <iostream>
#include <vector>
#include <stdexcept>

template<typename T>
class Stack {
    std::vector<T> data_;
public:
    void push(T v) { data_.push_back(std::move(v)); }
    void pop()      {
        if (empty()) throw std::runtime_error{"empty"};
        data_.pop_back();
    }
    const T& top() const { return data_.back(); }
    bool empty() const  { return data_.empty(); }
    size_t size() const { return data_.size(); }
};

int main() {
    Stack<int> s;
    s.push(1); s.push(2); s.push(3);
    std::cout << s.top() << "\n"; // 3
    s.pop();
    std::cout << s.top() << "\n"; // 2
}

2. Member Function Templates

A class template can have member function templates with their own parameters:

memfn.cpp
template<typename T>
class Box {
    T value_;
public:
    Box(T v) : value_(std::move(v)) {}

    template<typename U>
    bool equals(const U& o) const { return value_ == o; }
};

// Box<int>{5}.equals(5.0)  // mixes types

3. Full Specialization

Provide a custom implementation for a specific type:

spec.cpp
template<typename T>
class Storage { /* generic */ };

template<>
class Storage<bool> { /* compact bit packed */ };

This is exactly how std::vector<bool> is implemented (controversially).

4. Partial Specialization

For a specific pattern of types:

partial.cpp
template<typename A, typename B>
struct Pair { A a; B b; };

// Partial: when both types are the same
template<typename T>
struct Pair<T, T> { T a, b; bool sameType = true; };

5. Non-type Template Parameters

ntype.cpp
template<typename T, size_t N>
class FixedArray {
    T data_[N];
public:
    size_t size() const { return N; }
    T& operator[](size_t i) { return data_[i]; }
};
// FixedArray<int, 100> arr;

6. Practice Problems

  1. Write a generic Pair<A, B> class with two members.
    ✨ Show Answer
    template<typename A, typename B>
    struct Pair { A first; B second; };
  2. Build a Queue<T> on top of std::deque.
    ✨ Show Answer
    template<typename T>
    class Queue {
        std::deque<T> d_;
    public:
        void enqueue(T v) { d_.push_back(std::move(v)); }
        T dequeue() { T v = std::move(d_.front()); d_.pop_front(); return v; }
        bool empty() const { return d_.empty(); }
    };
  3. Why are std::vector<int> and std::vector<double> different types?
    ✨ Show Answer

    Each instantiation is a separate class. They share the source template but produce distinct compiled types — incompatible by assignment.

  4. Specialize Stack<bool> to use bit packing (sketch).
    ✨ Show Answer
    template<>
    class Stack<bool> {
        std::vector<uint64_t> bits_;
        size_t n_ = 0;
        // pack each bool into 1 bit
    };
  5. Write a class template with a default type argument.
    ✨ Show Answer
    template<typename T = int>
    struct X { T v{}; };
    X<> x; // T = int
  6. Add a member function template that converts to another type.
    ✨ Show Answer
    template<typename T>
    class Box {
        T v_;
    public:
        template<typename U>
        Box<U> convert() const { return Box<U>{static_cast<U>(v_)}; }
    };
  7. Why are most STL containers class templates?
    ✨ Show Answer

    To work for any element type without runtime cost. std::vector<Person> stores Persons directly, with no boxing.

  8. Where should the implementation of class template members go?
    ✨ Show Answer

    In the header, after the class definition. Or inline inside the class body. Never in a .cpp (compiler can't see them).

  9. Define a generic Stack with maximum N (non-type param).
    ✨ Show Answer
    template<typename T, size_t N>
    class FixedStack { T data_[N]; size_t n_ = 0; };
  10. Compile error if you instantiate a Stack with a non-comparable type — when does it fire?
    ✨ Show Answer

    When you call a member that uses the missing operator. Templates use "duck typing" — instantiation only checks the operations actually used.

  11. Use template alias.
    ✨ Show Answer
    template<typename T>
    using Vec = std::vector<T>;
    Vec<int> v;
  12. Why do partial specializations exist?
    ✨ Show Answer

    To customize behavior for a pattern of types (e.g. all pointer types, all containers). Used heavily by STL traits.

  13. Specialize for pointers.
    ✨ Show Answer
    template<typename T> struct isPointer { static constexpr bool value = false; };
    template<typename T> struct isPointer<T*> { static constexpr bool value = true; };
  14. Why might too many template instantiations slow compilation?
    ✨ Show Answer

    Each instantiation is fresh code generation. A heavy templated codebase can have millions of lines after instantiation. Solutions: explicit instantiation in a single .cpp.

  15. Use std::array<T, N> — what kind of params does it have?
    ✨ Show Answer

    One type parameter (T), one non-type parameter (size_t N).

  16. Compare class template std::pair<A, B> with our Pair.
    ✨ Show Answer

    std::pair adds: structured binding support, operator<=>, std::make_pair, hashing... Your hand-rolled Pair lacks all of that.

Summary

Class templates make data structures generic. Each instantiation is a separate type. Specialization (full or partial) lets you customize behavior for specific types or type patterns. Non-type parameters allow sizes and other values as template arguments. Implementations live in headers.

Next Module → STL Containers: vector, list, deque, map, set.