Шаблоны функций в C++: как писать универсальный код без дублирования
Поисковый запрос, на который отвечает эта статья: «шаблоны функций C++ для начинающих». Шаблоны функций в C++ нужны, когда логика одна и та же, а типы данных разные. Вместо трёх функций для int, double и std::string можно написать одну универсальную функцию.
Зачем нужны шаблоны функций
Представьте, что вам нужна функция, возвращающая большее из двух значений. Без шаблонов код быстро начинает дублироваться:
int maxInt(int a, int b) {
return a > b ? a : b;
}
double maxDouble(double a, double b) {
return a > b ? a : b;
}
Логика одинаковая, отличаются только типы. Шаблон функции решает эту проблему.
Простейший шаблон функции
Синтаксис начинается с ключевого слова template, после которого указывается параметр типа:
#include <iostream>
#include <string>
template <typename T>
T maxValue(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << maxValue(10, 25) << '\n'; // int
std::cout << maxValue(3.5, 2.1) << '\n'; // double
std::cout << maxValue(std::string("cat"), std::string("dog")) << '\n';
}
T — это не конкретный тип, а «заготовка». При вызове maxValue(10, 25) компилятор понимает, что T — это int. При вызове с double создаётся версия для double.
typename или class: что выбрать
В шаблонах функций можно писать и typename, и class:
template <typename T>
T square(T x) {
return x * x;
}
template <class T>
T cube(T x) {
return x * x * x;
}
Для параметра типа в обычном шаблоне функции разницы почти нет. В современном C++ чаще используют typename, потому что оно лучше передаёт смысл: параметр может быть не только классом, но и int, double, bool и т.д.
Вывод типа и явное указание типа
Обычно компилятор сам выводит тип:
std::cout << square(5) << '\n'; // T = int
std::cout << square(2.5) << '\n'; // T = double
Но иногда тип можно указать явно:
std::cout << square<double>(5) << '\n'; // 5 преобразуется в double
Явное указание полезно, когда нужно контролировать преобразования или компилятор не может однозначно вывести тип.
Шаблон с несколькими типами
Если аргументы могут быть разных типов, используйте несколько параметров шаблона:
#include <iostream>
template <typename T, typename U>
auto add(T a, U b) {
return a + b;
}
int main() {
std::cout << add(10, 2.5) << '\n'; // результат double
std::cout << add(3, 4) << '\n'; // результат int
}
Здесь возвращаемый тип указан как auto, и компилятор сам определяет результат выражения a + b.
Передача по const-ссылке
Для маленьких типов вроде int передача по значению нормальна. Но для строк, векторов и ваших классов копирование может быть дорогим. Часто шаблонные функции пишут так:
#include <iostream>
#include <string>
template <typename T>
void printTwice(const T& value) {
std::cout << value << ' ' << value << '\n';
}
int main() {
std::string name = "Alex";
printTwice(name);
printTwice(42);
}
const T& означает: «не копировать объект и не изменять его». Это хороший вариант для функций, которые только читают аргумент.
Практический пример: печать массива std::array
Шаблоны могут принимать не только типы, но и значения времени компиляции. Например, размер массива:
#include <array>
#include <iostream>
template <typename T, std::size_t N>
void printArray(const std::array<T, N>& arr) {
for (const auto& item : arr) {
std::cout << item << ' ';
}
std::cout << '\n';
}
int main() {
std::array<int, 3> numbers{1, 2, 3};
std::array<std::string, 2> words{"C++", "templates"};
printArray(numbers);
printArray(words);
}
Здесь T — тип элемента, а N — размер массива. Компилятор выводит оба параметра автоматически.
Частая ошибка: разные типы в одном T
Такой код может не скомпилироваться:
template <typename T>
T maxValue(T a, T b) {
return a > b ? a : b;
}
int main() {
auto x = maxValue(10, 2.5); // ошибка: T не может быть сразу int и double
}
Решения два: привести типы явно или сделать шаблон с двумя параметрами:
auto x = maxValue<double>(10, 2.5);
Или так:
template <typename T, typename U>
auto maxMixed(T a, U b) {
return a > b ? a : b;
}
Где хранить шаблонные функции
Важный момент для новичков: определения шаблонных функций обычно размещают в заголовочных файлах .h или .hpp, а не только в .cpp. Причина в том, что компилятору нужно видеть полный код шаблона в месте его использования, чтобы создать конкретную версию функции.
Советы и лучшие практики
- Используйте шаблоны, когда алгоритм одинаковый для разных типов.
- Не превращайте простой код в шаблонный без необходимости: читаемость важнее «универсальности».
- Для крупных объектов передавайте параметры как
const T&. - Проверяйте, какие операции требуются от типа: если внутри есть
a > b, тип должен поддерживать оператор>. - Если ошибки компиляции стали слишком длинными, упростите шаблон и проверьте его на одном типе.
Если вы хотите не просто выучить синтаксис, а уверенно применять шаблоны, STL, классы и современный C++ в реальных задачах, посмотрите курс «Программирование на C++ с Нуля до Гуру» — пошаговый путь от основ к уверенной практике.
Итог
Шаблоны функций в C++ позволяют писать универсальный код без дублирования. Они особенно полезны для математических функций, алгоритмов, работы с контейнерами и библиотечного кода. Главное — начинать с простых примеров, понимать вывод типов и помнить: шаблон работает только с теми типами, которые поддерживают используемые внутри операции.
-
Создано 28.08.2026 17:00:52
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.