Шаблон функции (function template) — заготовка функции, в которой тип оставлен параметром. Конкретную функцию компилятор создаёт сам, подставив тип, выведенный из аргументов вызова.
Зачем нужно
Swap для int, float, double отличаются только типом. Писать
перегрузку на каждый тип — копирование. Шаблон
описывает алгоритм один раз для любого подходящего типа.
Как работает
template <typename T>
void Swap(T& lhs, T& rhs) {
T tmp = lhs;
lhs = rhs;
rhs = tmp;
}
double lhs = 2, rhs = 1;
Swap(lhs, rhs); // T выведен как doubletemplate <typename T> объявляет параметр шаблона T. При вызове компилятор
смотрит на аргументы, выводит T и создаёт (инстанцирует) обычную функцию
Swap(double&, double&). Так шаблон превращается в перегрузку функции.
Не на этапе препроцессинга
В коде лекции сказано, что шаблоны превращаются в перегрузки «на этапе препроцессинга». Это неверно: препроцессор шаблоны не трогает (они видны в выводе
g++ -Eкак есть), инстанцирование делает компилятор. См. Компиляция и сборка.
Несколько параметров и явное указание
template <typename T, typename U>
void PrintValues(T x, U y) {
std::cout << "x=" << x << ' ' << "y=" << y << '\n';
}
PrintValues(rx, 0.0); // T = int, U = double
PrintValues<float>(1, 0.0); // T = float задан явно, U выведен: doubleПравила вывода
Типы выводятся для аргументов независимо. При выведении по значению
(параметр T x):
- отбрасываются
constиvolatile; - ссылочность аргумента теряется;
- массив низводится до указателя на первый элемент;
- функция низводится до указателя на функцию.
Поэтому для int& rx выводится T = int, для const int cx — тоже int,
для int a[3] — int*.
Не из лекции
- «Независимо» значит, что выводы должны совпасть:
Swap(a, c)сfloat aиdouble c— ошибкаdeduced conflicting types for parameter 'T'.- Правила 1–4 — для параметра по значению. В
Swap(T& lhs, T& rhs)ссылка сохраняет тип аргумента как есть.volatile— квалификатор вродеconst: объект может измениться в обход программы, поэтому компилятор не оптимизирует обращения к нему.- Проверено на GCC 16.2.
Тонкости
- Если подходят и обычная функция, и шаблон, при равных преобразованиях
выбирается обычная функция. В коде лекции
Swap(z, v)сfloatвызывает нешаблоннуюSwap(float&, float&), а дляdoubleработает шаблон. Tв теле — полноценный тип:T tmp = lhs;.
Частые ошибки
- Передать аргументы разных типов туда, где оба параметра
T. - Ждать, что
Tдляconst intвыведется какconst intпри передаче по значению. - Думать, что шаблон обрабатывает препроцессор.