Skip to content

Template (Generic Programming)

📖 Giới thiệu

Template cho phép viết code generic - một đoạn code có thể làm việc với nhiều kiểu dữ liệu khác nhau. Template giúp tái sử dụng code và tăng hiệu suất vì không cần ép kiểu.

Ví dụ đơn giản đầu tiên

cpp
#include <iostream>
using namespace std;

// Function template
template <typename T>
T tim_max(T a, T b) {
    return (a > b) ? a : b;
}

// Class template
template <typename T>
class Box {
private:
    T data;
    
public:
    Box(T value) : data(value) {}
    
    T get_data() {
        return data;
    }
    
    void set_data(T value) {
        data = value;
    }
    
    void print() {
        cout << "Box chứa: " << data << endl;
    }
};

int main() {
    // Sử dụng function template
    cout << "Max(10, 20): " << tim_max(10, 20) << endl;
    cout << "Max(3.5, 2.1): " << tim_max(3.5, 2.1) << endl;
    cout << "Max('a', 'z'): " << tim_max('a', 'z') << endl;
    
    // Sử dụng class template
    Box<int> box_int(42);
    Box<string> box_string("Hello Template!");
    
    box_int.print();
    box_string.print();
    
    return 0;
}

🔧 Cú pháp

Function Template

cpp
template <typename T>
T function_name(T param1, T param2) {
    // Implementation
}

// Hoặc với nhiều type parameters
template <typename T1, typename T2>
T1 function_name(T1 param1, T2 param2) {
    // Implementation
}

Class Template

cpp
template <typename T>
class ClassName {
private:
    T member_variable;
    
public:
    ClassName(T value);
    T get_value();
    void set_value(T value);
};

// Implementation ngoài class
template <typename T>
ClassName<T>::ClassName(T value) : member_variable(value) {}

Template Specialization

cpp
// Partial specialization
template <typename T>
class Container<T*> {
    // Specialized for pointer types
};

// Full specialization
template <>
class Container<bool> {
    // Specialized for bool type
};

🔬 Phân tích & Giải thích

Các loại Template

1. Function Template

cpp
template <typename T>
void swap_values(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

2. Class Template

cpp
template <typename T, int size>
class Array {
private:
    T data[size];
    
public:
    T& operator[](int index) {
        return data[index];
    }
    
    int get_size() const {
        return size;
    }
};

3. Variable Template (C++14)

cpp
template <typename T>
constexpr T pi = T(3.1415926535897932385);

4. Alias Template (C++11)

cpp
template <typename T>
using Vector = std::vector<T>;

💻 Ví dụ minh họa

Ví dụ 1: Generic Container Class

cpp
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

template <typename T, int CAPACITY = 10>
class DynamicArray {
private:
    T* data;
    int size;
    int capacity;
    
public:
    // Constructor
    DynamicArray() : size(0), capacity(CAPACITY) {
        data = new T[capacity];
        cout << "Tạo DynamicArray với capacity: " << capacity << endl;
    }
    
    // Copy constructor
    DynamicArray(const DynamicArray& other) : size(other.size), capacity(other.capacity) {
        data = new T[capacity];
        for (int i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
        cout << "Copy DynamicArray" << endl;
    }
    
    // Destructor
    ~DynamicArray() {
        delete[] data;
        cout << "Hủy DynamicArray" << endl;
    }
    
    // Assignment operator
    DynamicArray& operator=(const DynamicArray& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            capacity = other.capacity;
            data = new T[capacity];
            for (int i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
        }
        return *this;
    }
    
    // Thêm phần tử
    void push_back(const T& value) {
        if (size >= capacity) {
            resize();
        }
        data[size++] = value;
        cout << "Thêm: " << value << " (Size: " << size << ")" << endl;
    }
    
    // Xóa phần tử cuối
    void pop_back() {
        if (size > 0) {
            cout << "Xóa: " << data[--size] << " (Size: " << size << ")" << endl;
        } else {
            cout << "Array rỗng, không thể xóa!" << endl;
        }
    }
    
    // Truy cập phần tử
    T& operator[](int index) {
        if (index < 0 || index >= size) {
            throw out_of_range("Index ngoài phạm vi!");
        }
        return data[index];
    }
    
    const T& operator[](int index) const {
        if (index < 0 || index >= size) {
            throw out_of_range("Index ngoài phạm vi!");
        }
        return data[index];
    }
    
    // Getter methods
    int get_size() const { return size; }
    int get_capacity() const { return capacity; }
    bool is_empty() const { return size == 0; }
    bool is_full() const { return size == capacity; }
    
    // Tìm phần tử
    int find(const T& value) const {
        for (int i = 0; i < size; i++) {
            if (data[i] == value) {
                return i;
            }
        }
        return -1;  // Không tìm thấy
    }
    
    // Chèn phần tử tại vị trí
    void insert(int index, const T& value) {
        if (index < 0 || index > size) {
            throw out_of_range("Index ngoài phạm vi!");
        }
        
        if (size >= capacity) {
            resize();
        }
        
        // Dịch chuyển các phần tử
        for (int i = size; i > index; i--) {
            data[i] = data[i-1];
        }
        
        data[index] = value;
        size++;
        cout << "Chèn " << value << " tại vị trí " << index << endl;
    }
    
    // Xóa phần tử tại vị trí
    void remove(int index) {
        if (index < 0 || index >= size) {
            throw out_of_range("Index ngoài phạm vi!");
        }
        
        T removed_value = data[index];
        
        // Dịch chuyển các phần tử
        for (int i = index; i < size - 1; i++) {
            data[i] = data[i + 1];
        }
        
        size--;
        cout << "Xóa " << removed_value << " tại vị trí " << index << endl;
    }
    
    // Hiển thị tất cả phần tử
    void print() const {
        cout << "Array [" << size << "/" << capacity << "]: ";
        if (size == 0) {
            cout << "(rỗng)";
        } else {
            for (int i = 0; i < size; i++) {
                cout << data[i];
                if (i < size - 1) cout << ", ";
            }
        }
        cout << endl;
    }
    
    // Sắp xếp (bubble sort)
    void sort() {
        for (int i = 0; i < size - 1; i++) {
            for (int j = 0; j < size - i - 1; j++) {
                if (data[j] > data[j + 1]) {
                    T temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                }
            }
        }
        cout << "Đã sắp xếp array" << endl;
    }
    
private:
    void resize() {
        int new_capacity = capacity * 2;
        T* new_data = new T[new_capacity];
        
        for (int i = 0; i < size; i++) {
            new_data[i] = data[i];
        }
        
        delete[] data;
        data = new_data;
        capacity = new_capacity;
        
        cout << "Tăng capacity lên " << capacity << endl;
    }
};

// Function template để test
template <typename T>
void test_dynamic_array(const string& type_name) {
    cout << "\n=== TEST " << type_name << " ===" << endl;
    
    DynamicArray<T, 3> arr;  // Capacity ban đầu = 3
    
    // Test các operations cơ bản
    arr.print();
    
    if constexpr (is_same_v<T, int>) {
        arr.push_back(10);
        arr.push_back(20);
        arr.push_back(30);
        arr.push_back(40);  // Sẽ trigger resize
        
        arr.insert(1, 15);
        arr.print();
        
        arr.sort();
        arr.print();
        
        cout << "Tìm 20: vị trí " << arr.find(20) << endl;
        arr.remove(2);
        arr.print();
        
    } else if constexpr (is_same_v<T, string>) {
        arr.push_back("Hello");
        arr.push_back("World");
        arr.push_back("Template");
        arr.push_back("Programming");
        
        arr.print();
        arr.sort();
        arr.print();
        
    } else if constexpr (is_same_v<T, double>) {
        arr.push_back(3.14);
        arr.push_back(2.71);
        arr.push_back(1.41);
        
        arr.print();
        arr.sort();
        arr.print();
    }
}

int main() {
    cout << "=== DYNAMIC ARRAY TEMPLATE ===" << endl;
    
    // Test với các kiểu dữ liệu khác nhau
    test_dynamic_array<int>("INTEGER");
    test_dynamic_array<string>("STRING");
    test_dynamic_array<double>("DOUBLE");
    
    cout << "\n=== TEST COPY CONSTRUCTOR ===" << endl;
    DynamicArray<int> arr1;
    arr1.push_back(1);
    arr1.push_back(2);
    arr1.push_back(3);
    
    DynamicArray<int> arr2 = arr1;  // Copy constructor
    arr2.push_back(4);
    
    cout << "Array 1: ";
    arr1.print();
    cout << "Array 2: ";
    arr2.print();
    
    return 0;
}

Ví dụ 2: Generic Math Operations

cpp
#include <iostream>
#include <string>
#include <cmath>
#include <type_traits>
using namespace std;

// Function template với multiple parameters
template <typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
    return a + b;
}

// Template với SFINAE (Substitution Failure Is Not An Error)
template <typename T>
typename enable_if<is_arithmetic<T>::value, T>::type
absolute_value(T value) {
    return value < 0 ? -value : value;
}

// Template với constraint (C++20 concept style - simulated)
template <typename T>
T power(T base, int exponent) {
    static_assert(is_arithmetic<T>::value, "T must be arithmetic type");
    
    T result = 1;
    for (int i = 0; i < absolute_value(exponent); i++) {
        result *= base;
    }
    
    if (exponent < 0) {
        return T(1) / result;
    }
    return result;
}

// Variadic template
template <typename T>
T sum(T value) {
    return value;
}

template <typename T, typename... Args>
T sum(T first, Args... args) {
    return first + sum(args...);
}

// Template với default parameters
template <typename T, typename Compare = less<T>>
T find_extreme(T a, T b, Compare comp = Compare()) {
    return comp(a, b) ? b : a;
}

// Class template với specialization
template <typename T>
class Calculator {
public:
    static T add(T a, T b) {
        cout << "Generic add: ";
        return a + b;
    }
    
    static T multiply(T a, T b) {
        cout << "Generic multiply: ";
        return a * b;
    }
    
    static string get_type() {
        return "Generic type";
    }
};

// Specialization cho string
template <>
class Calculator<string> {
public:
    static string add(string a, string b) {
        cout << "String concatenation: ";
        return a + " " + b;
    }
    
    static string multiply(string a, int times) {
        cout << "String repeat: ";
        string result = "";
        for (int i = 0; i < times; i++) {
            result += a;
        }
        return result;
    }
    
    static string get_type() {
        return "String specialization";
    }
};

// Template với non-type parameters
template <typename T, int N>
class FixedArray {
private:
    T data[N];
    
public:
    FixedArray() {
        cout << "Tạo FixedArray với " << N << " phần tử kiểu " 
             << typeid(T).name() << endl;
    }
    
    T& operator[](int index) {
        if (index < 0 || index >= N) {
            throw out_of_range("Index out of range!");
        }
        return data[index];
    }
    
    constexpr int size() const {
        return N;
    }
    
    void fill(const T& value) {
        for (int i = 0; i < N; i++) {
            data[i] = value;
        }
    }
    
    void print() const {
        cout << "FixedArray: [";
        for (int i = 0; i < N; i++) {
            cout << data[i];
            if (i < N - 1) cout << ", ";
        }
        cout << "]" << endl;
    }
    
    // Iterator support (basic)
    T* begin() { return data; }
    T* end() { return data + N; }
    const T* begin() const { return data; }
    const T* end() const { return data + N; }
};

// Template alias
template <typename T>
using Matrix2D = FixedArray<FixedArray<T, 2>, 2>;

template <typename T>
using Vector3D = FixedArray<T, 3>;

int main() {
    cout << "=== TEMPLATE MATH OPERATIONS ===" << endl;
    
    // Function templates
    cout << "\n--- Function Templates ---" << endl;
    cout << "add(10, 20): " << add(10, 20) << endl;
    cout << "add(3.5, 2.1): " << add(3.5, 2.1) << endl;
    cout << "add(string, int): " << add(string("Value: "), 42) << endl;
    
    cout << "absolute_value(-15): " << absolute_value(-15) << endl;
    cout << "absolute_value(3.14): " << absolute_value(3.14) << endl;
    
    cout << "power(2, 3): " << power(2, 3) << endl;
    cout << "power(2.0, -2): " << power(2.0, -2) << endl;
    
    // Variadic templates
    cout << "\n--- Variadic Templates ---" << endl;
    cout << "sum(1, 2, 3, 4, 5): " << sum(1, 2, 3, 4, 5) << endl;
    cout << "sum(1.1, 2.2, 3.3): " << sum(1.1, 2.2, 3.3) << endl;
    
    // Templates với function objects
    cout << "\n--- Template với Function Objects ---" << endl;
    cout << "max(10, 20): " << find_extreme(10, 20) << endl;
    cout << "min(10, 20): " << find_extreme(10, 20, greater<int>()) << endl;
    
    // Class template specialization
    cout << "\n--- Class Template Specialization ---" << endl;
    Calculator<int> int_calc;
    cout << int_calc.get_type() << endl;
    cout << int_calc.add(10, 20) << endl;
    cout << int_calc.multiply(5, 6) << endl;
    
    Calculator<string> string_calc;
    cout << string_calc.get_type() << endl;
    cout << string_calc.add("Hello", "World") << endl;
    cout << string_calc.multiply("Hi! ", 3) << endl;
    
    // Non-type template parameters
    cout << "\n--- Non-type Template Parameters ---" << endl;
    FixedArray<int, 5> int_array;
    int_array.fill(42);
    int_array[2] = 100;
    int_array.print();
    
    FixedArray<string, 3> string_array;
    string_array[0] = "C++";
    string_array[1] = "Template";
    string_array[2] = "Programming";
    string_array.print();
    
    // Template aliases
    cout << "\n--- Template Aliases ---" << endl;
    Vector3D<double> position;
    position[0] = 1.0;  // x
    position[1] = 2.0;  // y
    position[2] = 3.0;  // z
    cout << "Position vector: ";
    position.print();
    
    Matrix2D<int> matrix;
    matrix[0][0] = 1; matrix[0][1] = 2;
    matrix[1][0] = 3; matrix[1][1] = 4;
    cout << "2x2 Matrix:" << endl;
    matrix[0].print();
    matrix[1].print();
    
    // Range-based for với template (C++11)
    cout << "\n--- Range-based For với Template ---" << endl;
    FixedArray<char, 5> char_array;
    char_array[0] = 'H';
    char_array[1] = 'e';
    char_array[2] = 'l';
    char_array[3] = 'l';
    char_array[4] = 'o';
    
    cout << "Characters: ";
    for (const auto& c : char_array) {
        cout << c;
    }
    cout << endl;
    
    return 0;
}

Ví dụ 3: Template Meta-programming

cpp
#include <iostream>
#include <type_traits>
using namespace std;

// Template recursion để tính factorial tại compile time
template <int N>
struct Factorial {
    static constexpr int value = N * Factorial<N-1>::value;
};

// Specialization để dừng recursion
template <>
struct Factorial<0> {
    static constexpr int value = 1;
};

// Template để check type traits
template <typename T>
void analyze_type() {
    cout << "\n=== PHÂN TÍCH KIỂU " << typeid(T).name() << " ===" << endl;
    
    cout << "Is arithmetic: " << is_arithmetic<T>::value << endl;
    cout << "Is integral: " << is_integral<T>::value << endl;
    cout << "Is floating point: " << is_floating_point<T>::value << endl;
    cout << "Is pointer: " << is_pointer<T>::value << endl;
    cout << "Is reference: " << is_reference<T>::value << endl;
    cout << "Is const: " << is_const<T>::value << endl;
    cout << "Size: " << sizeof(T) << " bytes" << endl;
}

// SFINAE với enable_if
template <typename T>
typename enable_if<is_integral<T>::value, void>::type
print_integer_info(T value) {
    cout << value << " là số nguyên" << endl;
    cout << "Giá trị min: " << numeric_limits<T>::min() << endl;
    cout << "Giá trị max: " << numeric_limits<T>::max() << endl;
}

template <typename T>
typename enable_if<is_floating_point<T>::value, void>::type
print_float_info(T value) {
    cout << value << " là số thực" << endl;
    cout << "Precision: " << numeric_limits<T>::digits10 << " digits" << endl;
    cout << "Min: " << numeric_limits<T>::min() << endl;
    cout << "Max: " << numeric_limits<T>::max() << endl;
}

// Template với conditional
template <typename T>
using larger_type = conditional_t<sizeof(T) >= 4, T, int>;

// Perfect forwarding
template <typename T>
void perfect_forward_wrapper(T&& arg) {
    cout << "Forwarding argument..." << endl;
    // forward<T>(arg) would be used in real code
}

int main() {
    cout << "=== TEMPLATE META-PROGRAMMING ===" << endl;
    
    // Compile-time calculation
    cout << "\n--- Compile-time Calculations ---" << endl;
    cout << "Factorial<5>: " << Factorial<5>::value << endl;
    cout << "Factorial<10>: " << Factorial<10>::value << endl;
    
    // Type analysis
    analyze_type<int>();
    analyze_type<double>();
    analyze_type<int*>();
    analyze_type<const char*>();
    
    // SFINAE demonstrations
    cout << "\n--- SFINAE Examples ---" << endl;
    print_integer_info(42);
    print_float_info(3.14159);
    
    // Type conditionals
    cout << "\n--- Type Conditionals ---" << endl;
    larger_type<char> lc = 100;      // becomes int
    larger_type<int> li = 1000;      // stays int
    larger_type<long> ll = 10000L;   // stays long
    
    cout << "larger_type<char> size: " << sizeof(lc) << endl;
    cout << "larger_type<int> size: " << sizeof(li) << endl;
    cout << "larger_type<long> size: " << sizeof(ll) << endl;
    
    return 0;
}

🏋️ Thực hành

Bài tập 1: Generic Stack

Tạo class template Stack<T> với:

  • push(), pop(), top(), empty(), size()
  • Exception handling cho stack empty/full

Bài tập 2: Template Sort Functions

Tạo function template cho các thuật toán sắp xếp:

  • bubble_sort(), selection_sort(), insertion_sort()
  • Tham số template cho comparator

Bài tập 3: Smart Pointer Template

Tạo class template SmartPtr<T> với:

  • Automatic memory management
  • Reference counting
  • Copy constructor và assignment operator

📋 Tóm tắt

  1. Template: Generic programming trong C++

  2. Function Template: Hàm làm việc với nhiều kiểu dữ liệu

  3. Class Template: Lớp làm việc với nhiều kiểu dữ liệu

  4. Template Specialization: Tùy chỉnh cho kiểu cụ thể

  5. Lợi ích:

    • Code reuse
    • Type safety
    • Performance (compile-time optimization)
    • Flexibility

Bài tiếp theo: Xử lý ngoại lệ - Tìm hiểu về error handling.

Khóa học C++ miễn phí