Шаблоны функций
Перегрузки twice(int) и twice(double) различаются только типом, а тело у них одно: return x * 2;. Для long и float понадобились бы ещё две такие же копии. Шаблон функции (function template) записывает тело один раз, а версии для нужных типов компилятор (compiler) создаёт сам:
#include <iostream>
template <typename T>
T twice(T x) {
return x * 2;
}
int main() {
std::cout << twice(21) << ' ' << twice(1.25) << '\n'; // 42 2.5
}Параметр шаблона
Шаблон функции — заготовка, по которой компилятор строит обычные функции. Строка template <typename T> объявляет параметр шаблона (template parameter) T — имя, которое обозначает тип. В параметрах, типе результата и теле T стоит там, где у обычной функции был бы int или double. Имя T — соглашение, подойдёт и Value, а вместо typename можно написать class.
Инстанцирование
Для вызова twice(21) компилятор подставляет int вместо T и получает функцию int twice(int x). Это инстанцирование (instantiation): по шаблону создаётся функция для конкретного типа. Вызов twice(1.25) создаёт вторую функцию — double twice(double x). Ещё один вызов с int использует уже созданную версию.
Тело шаблона компилятор проверяет для каждого типа отдельно. Для int и double выражение x * 2 допустимо. А строку std::string на 2 не умножить, поэтому вызов twice(std::string("ab")) не скомпилируется, хотя сам шаблон написан верно.
Шаблоны в заголовках
Чтобы создать версию для int, компилятору нужно тело шаблона, одного объявления (declaration) мало. Поэтому шаблон обычно пишут в заголовочном файле (header) twice.h целиком, вместе с телом:
#pragma once
template <typename T>
T twice(T x) {
return x * 2;
}Если подключить такой заголовок в два .cpp-файла, определение (definition) обычной функции привело бы к ошибке multiple definition, а шаблону это не грозит. Правило одного определения (one definition rule) разрешает одинаковые определения шаблона в разных единицах трансляции (translation units), как и у inline-функций.
Частая ошибка
Шаблон разделили, как обычную функцию: в twice.h оставили объявление template <typename T> T twice(T x);, а тело перенесли в twice.cpp. Оба файла компилируются, но сборка (build) clang++ main.cpp twice.cpp не проходит:
#include <iostream>
#include "twice.h"
int main() {
std::cout << twice(21) << '\n'; // ✗
}undefined reference to `int twice<int>(int)'Это сообщение компоновщика (linker), в задачах курса — undefined symbol. В twice.cpp нет вызова с int, поэтому версия для int там не создаётся. В main.cpp нет тела шаблона, и создать её здесь тоже нельзя. Перенесите тело в twice.h.