Giao diện
🔧 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 intsDefault 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;
}Print Container
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
| Feature | Syntax |
|---|---|
| Basic template | template<typename T> |
| Multiple params | template<typename T, typename U> |
| Non-type param | template<typename T, int N> |
| Default | template<typename T = int> |
| Variadic | template<typename... Args> |
| Fold expression | (args + ...) (C++17) |
➡️ Tiếp theo
Tiếp theo: Class Templates — Generic classes.
🧠 Quiz
Câu 1: Đoạn code sau, compiler deduce T là gì?
cpp
template<typename T>
T add(T a, T b) { return a + b; }
auto result = add(5, 3.0);- [ ] A)
T = int - [ ] B)
T = double - [x] C) Compilation error — T không thể deduce khi arguments khác type
- [ ] D)
T = auto
💡 Giải thích: Khi gọi
add(5, 3.0), argument đầu làint, argument sau làdouble. Compiler không thể deduce một T duy nhất cho cả hai → compilation error. Giải pháp: dùngadd<double>(5, 3.0)(explicit), hoặc dùng 2 template paramstemplate<typename T, typename U>.
Câu 2: Template instantiation xảy ra lúc nào?
cpp
template<typename T>
T square(T x) { return x * x; }
int a = square(5);
double b = square(3.14);- [x] A) Compile time — compiler tạo riêng
square<int>vàsquare<double> - [ ] B) Runtime — function tự detect type khi chạy
- [ ] C) Link time — linker kết hợp các versions
- [ ] D) Không tạo function mới, chỉ cast tự động
💡 Giải thích: Template instantiation là compile-time code generation. Compiler tạo ra 2 functions riêng biệt:
square<int>vàsquare<double>trong binary. Không có runtime overhead — giống như bạn viết tay 2 functions. Đây là zero-cost abstraction của C++ templates.
Câu 3: SFINAE trong C++ nghĩa là gì?
- [ ] A) Substitution Failure Always Notifies An Error
- [x] B) Substitution Failure Is Not An Error — compiler thử overload khác
- [ ] C) Special Function Inheritance Not Allowed in Expressions
- [ ] D) Static Function Inlining for Non-Abstract Entities
💡 Giải thích: SFINAE (Substitution Failure Is Not An Error) — khi compiler thử substitute template parameters mà thất bại, nó không báo lỗi mà âm thầm loại bỏ overload đó và thử các overload khác. Đây là cơ chế cho phép
std::enable_ifhoạt động, giúp "chọn lọc" template nào được sử dụng dựa trên type traits.