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
Template: Generic programming trong C++
Function Template: Hàm làm việc với nhiều kiểu dữ liệu
Class Template: Lớp làm việc với nhiều kiểu dữ liệu
Template Specialization: Tùy chỉnh cho kiểu cụ thể
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.