Skip to content

🔧 Function Templates — Generic Functions

Function templates cho phép viết một function hoạt động với nhiều types khác nhau.

Cú pháp cơ bản

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

// Hoặc dùng 'class' thay cho 'typename' (tương đương)
template<class T>
T multiply(T a, T b) {
    return a * b;
}

Sử dụng

cpp
#include <iostream>
#include <string>

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

int main() {
    // Explicit type specification
    int x = add<int>(5, 3);           // 8
    double y = add<double>(2.5, 3.7); // 6.2
    
    // Type deduction (compiler tự suy ra)
    int a = add(5, 3);                // Deduced as add<int>
    double b = add(2.5, 3.7);         // Deduced as add<double>
    
    std::string s = add(std::string("Hello "), std::string("World"));
    // "Hello World"
    
    return 0;
}

Template Instantiation

┌─────────────────────────────────────────────────────────────────┐
│                 TEMPLATE INSTANTIATION                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Source code:          template<typename T> T add(T a, T b);   │
│                                    │                            │
│                                    ▼                            │
│  Compiler sees:       add(5, 3)   add(2.5, 3.7)   add(s1, s2)  │
│                           │            │              │         │
│                           ▼            ▼              ▼         │
│  Generates:           add<int>    add<double>   add<string>    │
│                                                                 │
│  Binary contains 3 separate functions!                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Đây là compile-time code generation — no runtime overhead!


Multiple Template Parameters

cpp
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// C++14 simplified (auto return type deduction)
template<typename T, typename U>
auto multiply(T a, U b) {
    return a * b;
}

int main() {
    auto result = add(5, 2.5);     // int + double = double
    auto result2 = multiply(3, 4.0); // int * double = double
    return 0;
}

Non-type Template Parameters

cpp
// N là compile-time constant
template<typename T, int N>
T arraySum(T (&arr)[N]) {
    T sum = T();
    for (int i = 0; i < N; ++i) {
        sum += arr[i];
    }
    return sum;
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int sum = arraySum(arr);  // N deduced as 5
    std::cout << sum << std::endl;  // 15
    return 0;
}

Non-type Parameter Examples

cpp
template<int N>
struct FixedBuffer {
    char data[N];
};

template<typename T, std::size_t Size>
class Array {
    T data_[Size];
public:
    constexpr std::size_t size() const { return Size; }
    T& operator[](std::size_t i) { return data_[i]; }
};

Array<int, 10> arr;  // Fixed-size array of 10 ints

Default Template Arguments

cpp
template<typename T = int, typename U = double>
auto compute(T a, U b) {
    return a + b;
}

int main() {
    auto r1 = compute(5, 3.0);        // T=int, U=double
    auto r2 = compute<float>(5, 3.0); // T=float, U=double
    auto r3 = compute<float, float>(5, 3); // T=float, U=float
    return 0;
}

Template Overloading

cpp
#include <iostream>
#include <cstring>

// Generic version
template<typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// Overload for C-strings (char*)
const char* maximum(const char* a, const char* b) {
    return (std::strcmp(a, b) > 0) ? a : b;
}

int main() {
    std::cout << maximum(10, 20) << std::endl;         // 20
    std::cout << maximum(3.14, 2.71) << std::endl;     // 3.14
    std::cout << maximum("apple", "banana") << std::endl; // banana
    return 0;
}

SFINAE (Substitution Failure Is Not An Error)

cpp
#include <type_traits>

// Enable chỉ cho integral types
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
increment(T value) {
    return value + 1;
}

// Enable chỉ cho floating point
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
increment(T value) {
    return value + 0.1;
}

int main() {
    auto a = increment(5);     // Calls integral version: 6
    auto b = increment(5.0);   // Calls floating point version: 5.1
    // increment("hello");     // ❌ Error: no matching function
    return 0;
}

📌 SFINAE

Nếu template substitution thất bại, compiler không báo lỗi mà thử overload khác. Đây là cách "chọn lọc" templates trước C++20.


Variadic Templates (C++11)

cpp
#include <iostream>

// Base case
void print() {
    std::cout << std::endl;
}

// Recursive case
template<typename T, typename... Args>
void print(T first, Args... rest) {
    std::cout << first << " ";
    print(rest...);  // Recursively expand
}

int main() {
    print(1, 2.5, "hello", 'a');
    // Output: 1 2.5 hello a
    return 0;
}

Fold Expressions (C++17)

cpp
// C++17: Much cleaner!
template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Unary right fold
}

template<typename... Args>
void printAll(Args... args) {
    ((std::cout << args << " "), ...);  // Comma fold
    std::cout << std::endl;
}

int main() {
    std::cout << sum(1, 2, 3, 4, 5) << std::endl;  // 15
    printAll(1, 2.5, "hello");  // 1 2.5 hello
    return 0;
}

Common Patterns

Swap

cpp
template<typename T>
void mySwap(T& a, T& b) {
    T temp = std::move(a);
    a = std::move(b);
    b = std::move(temp);
}

Max/Min

cpp
template<typename T>
const T& myMax(const T& a, const T& b) {
    return (a > b) ? a : b;
}
cpp
template<typename Container>
void printContainer(const Container& c) {
    for (const auto& elem : c) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

// Works with vector, list, array, deque, etc.
std::vector<int> v = {1, 2, 3};
printContainer(v);  // 1 2 3

📚 Tổng kết

FeatureSyntax
Basic templatetemplate<typename T>
Multiple paramstemplate<typename T, typename U>
Non-type paramtemplate<typename T, int N>
Defaulttemplate<typename T = int>
Variadictemplate<typename... Args>
Fold expression(args + ...) (C++17)

➡️ Tiếp theo

Tiếp theo: Class Templates — Generic classes.