Функциональные шаблоны
Шаблоны позволяют писать generic код, работающий с любыми типами.
▸Базовый пример
1template <typename T>2T max_val(T a, T b) {3 return (a > b) ? a : b;4}56int main() {7 std::cout << max_val(3, 7) << "\n"; // 78 std::cout << max_val(3.14, 2.71) << "\n"; // 3.149 std::cout << max_val('a', 'z') << "\n"; // z10}
▸Специализация шаблона
1// Общий шаблон2template <typename T>3class Printer {4public:5 void print(T value) {6 std::cout << "Value: " << value << "\n";7 }8};910// Частичная специализация для указателей11template <typename T>12class Printer<T*> {13public:14 void print(T* value) {15 if (value) {16 std::cout << "Pointer to: " << *value << "\n";17 } else {18 std::cout << "Null pointer\n";19 }20 }21};
Классные шаблоны
▸Обобщённый контейнер
1template <typename T, size_t N>2class FixedArray {3 T data[N];4 size_t count = 0;5public:6 void push(const T& value) {7 if (count < N) {8 data[count++] = value;9 }10 }1112 T& operator[](size_t index) { return data[index]; }13 const T& operator[](size_t index) const { return data[index]; }1415 size_t size() const { return count; }16 constexpr size_t capacity() const { return N; }17};1819FixedArray<int, 10> arr;20arr.push(1);21arr.push(2);
Variadic Templates
Variadic templates принимают произвольное количество аргументов.
1// Рекурсивный вариант2void print() {} // Базовый случай34template <typename T, typename... Args>5void print(const T& first, const Args&... rest) {6 std::cout << first << " ";7 print(rest...);8}910// Fold expressions (C++17)11template <typename... Args>12void print_all(const Args&... args) {13 ((std::cout << args << " "), ...);14}1516int main() {17 print(1, "hello", 3.14, 'c'); // 1 hello 3.14 c18 print_all(1, "hello", 3.14); // 1 hello 3.1419}
▸Forwarding references
1template <typename T>2void wrapper(T&& arg) {3 // arg — forwarding reference4 process(std::forward<T>(arg));5}
SFINAE
Substitution Failure Is Not An Error — механизм, при котором ошибка подстановки типа не является ошибкой компиляции.
▸std::enable_if
1template <typename T>2typename std::enable_if<std::is_integral<T>::value, T>::type3safe_divide(T a, T b) {4 if (b == 0) throw std::runtime_error("Division by zero");5 return a / b;6}78// Только для целочисленных типов9safe_divide(10, 3); // OK10safe_divide(10.0, 3.0); // Ошибка компиляции
▸if constexpr (C++17)
1template <typename T>2auto process(T value) {3 if constexpr (std::is_same_v<T, std::string>) {4 return value + " processed";5 } else if constexpr (std::is_arithmetic_v<T>) {6 return value * 2;7 } else {8 return value;9 }10}
Концепты (Concepts, C++20)
1template <typename T>2concept Numeric = std::is_arithmetic_v<T>;34template <Numeric T>5T square(T value) {6 return value * value;7}89// Или с requires10template <typename T>11requires std::is_integral_v<T>12T factorial(T n) {13 return n <= 1 ? 1 : n * factorial(n - 1);14}
Шаблонная метапрограммирование
1// Вычисление факториала на этапе компиляции2template <size_t N>3struct Factorial {4 static constexpr size_t value = N * Factorial<N - 1>::value;5};67template <>8struct Factorial<0> {9 static constexpr size_t value = 1;10};1112static_assert(Factorial<5>::value == 120);
Заключение
Шаблоны — это мощный инструмент generic-программирования в C++. Они позволяют создавать типобезопасный, производительный код. Variadic templates и SFINAE расширяют возможности, а Concepts (C++20) делают шаблоны более читаемыми.