cpp

class templates

01_class_templates.cpp⚙️
/**
 * ============================================================
 * C++ CLASS TEMPLATES
 * ============================================================
 * 
 * This file covers:
 * - Class template basics
 * - Template member functions
 * - Template specialization
 * - Partial specialization
 * - Templates with inheritance
 * - Static members in templates
 * 
 * Compile: g++ -std=c++17 -Wall 01_class_templates.cpp -o class_templates
 * Run: ./class_templates
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <type_traits>

using namespace std;

// ============================================================
// PART 1: BASIC CLASS TEMPLATE
// ============================================================

template <typename T>
class Box {
private:
    T value;
    
public:
    // Constructor
    Box(const T& v = T()) : value(v) {}
    
    // Getter
    T getValue() const { return value; }
    
    // Setter
    void setValue(const T& v) { value = v; }
    
    // Display
    void display() const {
        cout << "Box contains: " << value << endl;
    }
};

// ============================================================
// PART 2: CLASS TEMPLATE WITH MULTIPLE PARAMETERS
// ============================================================

template <typename K, typename V>
class Pair {
private:
    K key;
    V value;
    
public:
    Pair(const K& k, const V& v) : key(k), value(v) {}
    
    K getKey() const { return key; }
    V getValue() const { return value; }
    
    void setKey(const K& k) { key = k; }
    void setValue(const V& v) { value = v; }
    
    void display() const {
        cout << key << " => " << value << endl;
    }
};

// ============================================================
// PART 3: CLASS TEMPLATE WITH NON-TYPE PARAMETERS
// ============================================================

template <typename T, size_t CAPACITY>
class Stack {
private:
    T data[CAPACITY];
    size_t topIndex;
    
public:
    Stack() : topIndex(0) {}
    
    bool empty() const { return topIndex == 0; }
    bool full() const { return topIndex == CAPACITY; }
    size_t size() const { return topIndex; }
    size_t capacity() const { return CAPACITY; }
    
    void push(const T& value) {
        if (full()) {
            throw overflow_error("Stack is full");
        }
        data[topIndex++] = value;
    }
    
    T pop() {
        if (empty()) {
            throw underflow_error("Stack is empty");
        }
        return data[--topIndex];
    }
    
    const T& top() const {
        if (empty()) {
            throw underflow_error("Stack is empty");
        }
        return data[topIndex - 1];
    }
    
    void display() const {
        cout << "Stack [" << size() << "/" << CAPACITY << "]: ";
        for (size_t i = 0; i < topIndex; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
};

// ============================================================
// PART 4: MEMBER FUNCTION TEMPLATES
// ============================================================

template <typename T>
class Container {
private:
    vector<T> items;
    
public:
    void add(const T& item) {
        items.push_back(item);
    }
    
    size_t size() const { return items.size(); }
    
    // Member function template
    template <typename U>
    void addConverted(const U& item) {
        items.push_back(static_cast<T>(item));
    }
    
    // Template member for comparison with different container
    template <typename U>
    bool hasSameSize(const Container<U>& other) const {
        return size() == other.size();
    }
    
    void display() const {
        cout << "Container [" << items.size() << "]: ";
        for (const auto& item : items) {
            cout << item << " ";
        }
        cout << endl;
    }
};

// ============================================================
// PART 5: TEMPLATE SPECIALIZATION
// ============================================================

// Primary template
template <typename T>
class TypeInfo {
public:
    static string name() { return "unknown"; }
    static bool isNumeric() { return false; }
    static bool isPointer() { return false; }
};

// Full specialization for int
template <>
class TypeInfo<int> {
public:
    static string name() { return "int"; }
    static bool isNumeric() { return true; }
    static bool isPointer() { return false; }
};

// Full specialization for double
template <>
class TypeInfo<double> {
public:
    static string name() { return "double"; }
    static bool isNumeric() { return true; }
    static bool isPointer() { return false; }
};

// Full specialization for string
template <>
class TypeInfo<string> {
public:
    static string name() { return "string"; }
    static bool isNumeric() { return false; }
    static bool isPointer() { return false; }
};

// Partial specialization for pointers
template <typename T>
class TypeInfo<T*> {
public:
    static string name() { return "pointer to " + TypeInfo<T>::name(); }
    static bool isNumeric() { return false; }
    static bool isPointer() { return true; }
};

// ============================================================
// PART 6: PARTIAL SPECIALIZATION
// ============================================================

// Primary template
template <typename T1, typename T2>
class Wrapper {
public:
    void info() const {
        cout << "Generic Wrapper<T1, T2>" << endl;
    }
};

// Partial specialization: both same type
template <typename T>
class Wrapper<T, T> {
public:
    void info() const {
        cout << "Wrapper<T, T> - same types" << endl;
    }
};

// ⚠️ LEARNING NOTE: Partial specializations can cause AMBIGUITY!
// 
// PROBLEM: If we have both Wrapper<T,T> and Wrapper<T,int>, then
// Wrapper<int, int> matches BOTH equally well:
//   - Wrapper<T, T> where T=int ✓
//   - Wrapper<T, int> where T=int ✓
// The compiler can't choose → ERROR!
//
// SOLUTION: Add a FULL specialization for the ambiguous case.
// Full specialization (no template params) is MOST SPECIFIC and wins.

// Full specialization for <int, int> - resolves the ambiguity!
template <>
class Wrapper<int, int> {
public:
    void info() const {
        cout << "Wrapper<int, int> - FULL specialization (resolves ambiguity)" << endl;
    }
};

// Partial specialization: second is int
template <typename T>
class Wrapper<T, int> {
public:
    void info() const {
        cout << "Wrapper<T, int> - second is int" << endl;
    }
};

// Partial specialization: both pointers
template <typename T1, typename T2>
class Wrapper<T1*, T2*> {
public:
    void info() const {
        cout << "Wrapper<T1*, T2*> - both pointers" << endl;
    }
};

// ============================================================
// PART 7: TEMPLATE WITH DEFAULT PARAMETERS
// ============================================================

template <typename T, typename Allocator = allocator<T>>
class SimpleVector {
private:
    T* data;
    size_t sz;
    size_t cap;
    Allocator alloc;
    
public:
    SimpleVector(size_t initialCap = 10) 
        : sz(0), cap(initialCap) {
        data = alloc.allocate(cap);
    }
    
    ~SimpleVector() {
        for (size_t i = 0; i < sz; i++) {
            alloc.destroy(&data[i]);
        }
        alloc.deallocate(data, cap);
    }
    
    void push_back(const T& value) {
        if (sz >= cap) {
            // Simplified - no resize
            throw overflow_error("Vector full");
        }
        alloc.construct(&data[sz++], value);
    }
    
    T& operator[](size_t i) { return data[i]; }
    size_t size() const { return sz; }
};

// ============================================================
// PART 8: TEMPLATES AND INHERITANCE
// ============================================================

// Base template
template <typename T>
class BaseContainer {
protected:
    T value;
    
public:
    BaseContainer(const T& v) : value(v) {}
    virtual void display() const {
        cout << "Base: " << value << endl;
    }
    virtual ~BaseContainer() = default;
};

// Derived template inheriting from template
template <typename T>
class DerivedContainer : public BaseContainer<T> {
private:
    string label;
    
public:
    DerivedContainer(const T& v, const string& l) 
        : BaseContainer<T>(v), label(l) {}
    
    void display() const override {
        cout << label << ": " << this->value << endl;
    }
};

// Non-template derived from template
class IntContainer : public BaseContainer<int> {
public:
    IntContainer(int v) : BaseContainer<int>(v) {}
    
    void display() const override {
        cout << "Integer: " << value << endl;
    }
};

// ============================================================
// PART 9: STATIC MEMBERS IN TEMPLATES
// ============================================================

template <typename T>
class Counter {
private:
    T value;
    static int instanceCount;  // One per type!
    
public:
    Counter(const T& v = T()) : value(v) {
        instanceCount++;
    }
    
    ~Counter() {
        instanceCount--;
    }
    
    static int getCount() { return instanceCount; }
    T getValue() const { return value; }
};

// Static member definition (one per type)
template <typename T>
int Counter<T>::instanceCount = 0;

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ CLASS TEMPLATES" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Basic Class Template
    // ========================================================
    
    cout << "--- DEMO 1: BASIC CLASS TEMPLATE ---" << endl << endl;
    
    Box<int> intBox(42);
    Box<double> doubleBox(3.14159);
    Box<string> stringBox("Hello, Templates!");
    
    intBox.display();
    doubleBox.display();
    stringBox.display();
    
    cout << endl;

    // ========================================================
    // DEMO 2: Multiple Parameters
    // ========================================================
    
    cout << "--- DEMO 2: MULTIPLE PARAMETERS ---" << endl << endl;
    
    Pair<string, int> age("Alice", 30);
    Pair<int, double> coordinates(100, 3.14);
    
    age.display();
    coordinates.display();
    
    cout << endl;

    // ========================================================
    // DEMO 3: Non-Type Parameters
    // ========================================================
    
    cout << "--- DEMO 3: NON-TYPE PARAMETERS ---" << endl << endl;
    
    Stack<int, 5> intStack;
    intStack.push(10);
    intStack.push(20);
    intStack.push(30);
    intStack.display();
    
    cout << "Top: " << intStack.top() << endl;
    cout << "Pop: " << intStack.pop() << endl;
    intStack.display();
    
    Stack<string, 3> stringStack;
    stringStack.push("first");
    stringStack.push("second");
    stringStack.display();
    
    cout << endl;

    // ========================================================
    // DEMO 4: Member Function Templates
    // ========================================================
    
    cout << "--- DEMO 4: MEMBER TEMPLATES ---" << endl << endl;
    
    Container<double> doubles;
    doubles.add(1.5);
    doubles.add(2.5);
    doubles.addConverted(3);     // int -> double
    doubles.addConverted(4.9f);  // float -> double
    doubles.display();
    
    Container<int> ints;
    ints.add(1);
    ints.add(2);
    ints.add(3);
    ints.add(4);
    
    cout << "Same size? " << (doubles.hasSameSize(ints) ? "yes" : "no") << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 5: Full Specialization
    // ========================================================
    
    cout << "--- DEMO 5: FULL SPECIALIZATION ---" << endl << endl;
    
    cout << "TypeInfo<int>::name() = " << TypeInfo<int>::name() << endl;
    cout << "TypeInfo<double>::name() = " << TypeInfo<double>::name() << endl;
    cout << "TypeInfo<string>::name() = " << TypeInfo<string>::name() << endl;
    cout << "TypeInfo<char>::name() = " << TypeInfo<char>::name() << endl;
    
    cout << "\nTypeInfo<int>::isNumeric() = " << TypeInfo<int>::isNumeric() << endl;
    cout << "TypeInfo<string>::isNumeric() = " << TypeInfo<string>::isNumeric() << endl;
    
    cout << "\nTypeInfo<int*>::name() = " << TypeInfo<int*>::name() << endl;
    cout << "TypeInfo<int*>::isPointer() = " << TypeInfo<int*>::isPointer() << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 6: Partial Specialization
    // ========================================================
    
    cout << "--- DEMO 6: PARTIAL SPECIALIZATION ---" << endl << endl;
    
    Wrapper<double, string> w1;  // Uses generic Wrapper<T1, T2>
    Wrapper<int, int> w2;        // Uses FULL specialization (most specific)
    Wrapper<double, int> w3;     // Uses Wrapper<T, int>
    Wrapper<int*, double*> w4;   // Uses Wrapper<T1*, T2*>
    Wrapper<char, char> w5;      // Uses Wrapper<T, T> - same types
    
    // ⚠️ LEARNING NOTE: Template specialization priority:
    // 1. Full specialization (template<>) - MOST specific, always wins
    // 2. Partial specialization - more specific patterns preferred  
    // 3. Primary template - least specific, used as fallback
    
    w1.info();  // Generic
    w2.info();  // Full specialization for <int,int>
    w3.info();  // Second is int
    w4.info();  // Both pointers
    w5.info();  // Same types (Wrapper<T,T>)
    
    cout << endl;

    // ========================================================
    // DEMO 7: Templates and Inheritance
    // ========================================================
    
    cout << "--- DEMO 7: TEMPLATE INHERITANCE ---" << endl << endl;
    
    BaseContainer<double>* containers[] = {
        new BaseContainer<double>(3.14),
        new DerivedContainer<double>(2.72, "Euler"),
    };
    
    for (auto* c : containers) {
        c->display();
        delete c;
    }
    
    IntContainer intCont(42);
    intCont.display();
    
    cout << endl;

    // ========================================================
    // DEMO 8: Static Members
    // ========================================================
    
    cout << "--- DEMO 8: STATIC MEMBERS ---" << endl << endl;
    
    cout << "Initial counts:" << endl;
    cout << "Counter<int>: " << Counter<int>::getCount() << endl;
    cout << "Counter<double>: " << Counter<double>::getCount() << endl;
    
    {
        Counter<int> c1(1), c2(2), c3(3);
        Counter<double> d1(1.0);
        
        cout << "\nAfter creating c1, c2, c3 (int) and d1 (double):" << endl;
        cout << "Counter<int>: " << Counter<int>::getCount() << endl;
        cout << "Counter<double>: " << Counter<double>::getCount() << endl;
    }
    
    cout << "\nAfter scope ends:" << endl;
    cout << "Counter<int>: " << Counter<int>::getCount() << endl;
    cout << "Counter<double>: " << Counter<double>::getCount() << endl;
    
    cout << endl;

    // ========================================================
    // CLASS TEMPLATE SUMMARY
    // ========================================================
    
    cout << "--- CLASS TEMPLATE SYNTAX ---" << endl << endl;
    
    cout << "Basic:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "template <typename T>" << endl;
    cout << "class MyClass { T member; };" << endl;
    
    cout << "\nNon-type parameter:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "template <typename T, size_t N>" << endl;
    cout << "class Array { T data[N]; };" << endl;
    
    cout << "\nFull specialization:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "template <>" << endl;
    cout << "class MyClass<int> { ... };" << endl;
    
    cout << "\nPartial specialization:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "template <typename T>" << endl;
    cout << "class MyClass<T*> { ... };" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "CLASS TEMPLATES COMPLETE!" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Create a template Queue<T, SIZE>:
 *    - Fixed-size circular queue
 *    - enqueue(), dequeue(), front(), back()
 *    - empty(), full(), size()
 * 
 * 2. Create a SmartArray<T> template:
 *    - Dynamic array with bounds checking
 *    - Copy and move semantics
 *    - Iterator support
 * 
 * 3. Create specialized Matrix<T>:
 *    - General template for any type
 *    - Specialized for bool (bit matrix)
 *    - Operations: add, multiply, transpose
 * 
 * 4. Create a template linked list:
 *    - Node<T> and List<T>
 *    - insert, remove, find
 *    - Iterator class
 */
Class Templates - C++ Tutorial | DeepML