Giao diện
🎯 Template Specialization — Handle Edge Cases
Specialization cho phép customize behavior cho specific types — xử lý edge cases mà generic template không cover.
Tại sao cần Specialization?
cpp
template<typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maximum(10, 20) << std::endl; // ✅ 20
std::cout << maximum(3.14, 2.71) << std::endl; // ✅ 3.14
// ❌ C-strings compare POINTERS, not content!
const char* s1 = "apple";
const char* s2 = "banana";
std::cout << maximum(s1, s2) << std::endl; // Compares addresses!
return 0;
}Giải pháp: Specialize cho const char*
Full Specialization (Function)
cpp
#include <iostream>
#include <cstring>
// Primary template
template<typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Full specialization for const char*
template<>
const char* maximum<const char*>(const char* a, const char* b) {
return (std::strcmp(a, b) > 0) ? a : b;
}
int main() {
std::cout << maximum(10, 20) << std::endl; // 20
const char* s1 = "apple";
const char* s2 = "banana";
std::cout << maximum(s1, s2) << std::endl; // banana ✅
return 0;
}Full Specialization (Class)
cpp
#include <iostream>
#include <cstring>
// Primary template
template<typename T>
class Printer {
public:
static void print(const T& value) {
std::cout << "Generic: " << value << std::endl;
}
};
// Full specialization for bool
template<>
class Printer<bool> {
public:
static void print(bool value) {
std::cout << "Bool: " << (value ? "true" : "false") << std::endl;
}
};
// Full specialization for const char*
template<>
class Printer<const char*> {
public:
static void print(const char* value) {
std::cout << "C-String: \"" << value << "\"" << std::endl;
}
};
int main() {
Printer<int>::print(42); // Generic: 42
Printer<double>::print(3.14); // Generic: 3.14
Printer<bool>::print(true); // Bool: true
Printer<const char*>::print("hi"); // C-String: "hi"
return 0;
}Partial Specialization
Partial specialization chỉ áp dụng cho class templates (không cho function templates).
Example: Pointer Specialization
cpp
#include <iostream>
// Primary template
template<typename T>
class TypeInfo {
public:
static void describe() {
std::cout << "Regular type" << std::endl;
}
};
// Partial specialization for pointers
template<typename T>
class TypeInfo<T*> {
public:
static void describe() {
std::cout << "Pointer to ";
TypeInfo<T>::describe();
}
};
// Partial specialization for references
template<typename T>
class TypeInfo<T&> {
public:
static void describe() {
std::cout << "Reference to ";
TypeInfo<T>::describe();
}
};
int main() {
TypeInfo<int>::describe(); // Regular type
TypeInfo<int*>::describe(); // Pointer to Regular type
TypeInfo<int**>::describe(); // Pointer to Pointer to Regular type
TypeInfo<int&>::describe(); // Reference to Regular type
return 0;
}Example: Array Specialization
cpp
template<typename T>
class Container {
T data_;
public:
Container(const T& d) : data_(d) {}
void print() { std::cout << "Single: " << data_ << std::endl; }
};
// Partial specialization for arrays
template<typename T, std::size_t N>
class Container<T[N]> {
T data_[N];
public:
Container(const T (&arr)[N]) {
std::copy(std::begin(arr), std::end(arr), data_);
}
void print() {
std::cout << "Array of " << N << ": ";
for (const auto& elem : data_) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
};
int main() {
Container<int> single(42);
single.print(); // Single: 42
int arr[] = {1, 2, 3, 4, 5};
Container<int[5]> array(arr);
array.print(); // Array of 5: 1 2 3 4 5
return 0;
}Partial Specialization Patterns
One of Multiple Parameters
cpp
template<typename T, typename U>
class Pair {
public:
static void describe() {
std::cout << "Generic Pair" << std::endl;
}
};
// Specialize when both types are the same
template<typename T>
class Pair<T, T> {
public:
static void describe() {
std::cout << "Same-type Pair" << std::endl;
}
};
// Specialize when second is int
template<typename T>
class Pair<T, int> {
public:
static void describe() {
std::cout << "Pair with int second" << std::endl;
}
};
int main() {
Pair<double, std::string>::describe(); // Generic Pair
Pair<int, int>::describe(); // Same-type Pair
Pair<std::string, int>::describe(); // Pair with int second
return 0;
}Type Traits Implementation
cpp
#include <iostream>
// Primary: not a pointer
template<typename T>
struct is_pointer {
static constexpr bool value = false;
};
// Partial specialization: IS a pointer
template<typename T>
struct is_pointer<T*> {
static constexpr bool value = true;
};
// Helper variable template (C++14)
template<typename T>
constexpr bool is_pointer_v = is_pointer<T>::value;
int main() {
std::cout << std::boolalpha;
std::cout << is_pointer_v<int> << std::endl; // false
std::cout << is_pointer_v<int*> << std::endl; // true
std::cout << is_pointer_v<int**> << std::endl; // true
return 0;
}More Type Traits
cpp
// Remove const
template<typename T>
struct remove_const {
using type = T;
};
template<typename T>
struct remove_const<const T> {
using type = T;
};
template<typename T>
using remove_const_t = typename remove_const<T>::type;
// Remove reference
template<typename T>
struct remove_reference {
using type = T;
};
template<typename T>
struct remove_reference<T&> {
using type = T;
};
template<typename T>
struct remove_reference<T&&> {
using type = T;
};
template<typename T>
using remove_reference_t = typename remove_reference<T>::type;Priority Order
Compiler chọn theo thứ tự ưu tiên:
┌─────────────────────────────────────────────────────────────────┐
│ SPECIALIZATION PRIORITY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Non-template function (exact match) │
│ 2. Full specialization │
│ 3. Partial specialization (more specific wins) │
│ 4. Primary template │
│ │
│ Example: │
│ Template\<T, U\> ← Primary │
│ Template\<T, T\> ← Partial (same types) │
│ Template\<T, int\> ← Partial (int second) │
│ Template\<int, int\> ← Full specialization │
│ │
│ Template\<int, int\> wins over Template\<T, T\>! │
│ │
└─────────────────────────────────────────────────────────────────┘Real-world Example: std::vector<bool>
cpp
// This is why std::vector<bool> is "special" (and controversial)
// Primary template (simplified)
template<typename T>
class vector {
T* data_;
// Normal storage
};
// Specialization for bool — packed bits
template<>
class vector<bool> {
unsigned char* data_;
// 8 bools per byte — space efficient but different behavior!
};
// ⚠️ Đây là lý do vector<bool> controversial:
// - operator[] không trả về bool&, mà proxy object
// - Không thể lấy pointer tới element📚 Tổng kết
| Type | Syntax | Use Case |
|---|---|---|
| Full (function) | template<> T func<Type>(...) | Specific type handling |
| Full (class) | template<> class Foo<Type> | Complete rewrite |
| Partial | template<typename T> class Foo<T*> | Pattern matching |
| Type traits | Remove const/ref/pointer | Metaprogramming |
⚠️ Avoid Over-specialization
Quá nhiều specializations làm code khó maintain. Ưu tiên dùng concepts (C++20) thay vì SFINAE + specialization.
➡️ Tiếp theo
Tiếp theo: C++20 Concepts — Modern template constraints.