Этот сайт использует cookies. Продолжение работы с сайтом означает, что Вы согласны!
Шаблонные (обобщенные) функции
Если присмотреться к реализации функции sum()
, то можно заметить, что вне зависимости от типа параметров внутри функции будет одна и та же инструкция return x + y;
, так как оператор +
позволяет сложить числа любого типа. Для таких случаев в языке C++ вместо перегрузки функции следует применять шаблонные функции, которые называют также обобщенными. Компилятор на основе шаблонной функции автоматически создаст перегруженные версии функции в зависимости от имеющихся способов ее вызова в программе. Описывается шаблонная функция по следующей схеме:
template<typename Тип1[, ..., typename ТипN]>
Тип Название_функции(Тип Название_переменной1
[, ..., Тип Название_переменнойN])
{
Тело_функции
[return[ Возвращаемое_значение];]
}
После ключевого слова template
внутри угловых скобок через запятую указываются обобщенные названия типов или реальные. Обобщенные названия используются для описания типов параметров и могут использоваться внутри функции. При компиляции обобщенные типы будут заменены реальными типами данных. Перед названием обобщенного типа могут быть указаны ключевые слова typename
или class
, которые обозначают одно и то же. Остальное описание шаблонной функции совпадает с описанием обычной функции, только вместо реальных типов данных указываются названия обобщенных типов, перечисленных после ключевого слова template
.
При вызове шаблонной функции после названия функции внутри угловых скобок можно указать реальные типы данных через запятую:
Название_функции<Тип1, ..., ТипN>(Параметры)
В большинстве случаев компилятор может самостоятельно определить реальные типы данных по типу передаваемых параметров, поэтому шаблонную функцию можно вызвать как обычную функцию:
Название_функции(Параметры)
В качестве примера создадим шаблонную функцию для сложения двух чисел любых одинаковых типов (листинг 12.18).
Листинг 12.18. Шаблонные функции
#include <iostream>
template<typename T>
T sum(T x, T y);
int main() {
// Явное указание типа T
std::cout << sum<int>(10, 20) << std::endl; // 30
std::cout << sum<double>(10.5, 20.4) << std::endl; // 30.9
std::cout << sum<float>(10.5f, 20.7f) << std::endl; // 31.2
// Автоматическое выведение типа
std::cout << sum(10, 20) << std::endl; // 30
std::cout << sum(10.5, 20.4) << std::endl; // 30.9
std::cout << sum(10.5f, 20.7f) << std::endl; // 31.2
return 0;
}
template<typename T>
T sum(T x, T y) {
return x + y;
}
Компилятор на основе шаблонной функции и способах ее вызова автоматически создаст следующие перегруженные версии функции:
int sum(int x, int y);
float sum(float x, float y);
double sum(double x, double y);
Шаблонная функция sum()
из листинга 12.18 позволяет складывать числа только одного типа. Чтобы можно было складывать числа разных типов, например, int
и double
, следует объявить два разных обобщенных типа:
template<typename T1, typename T2>
T1 sum(T1 x, T2 y) {
return x + y;
}
Пример вызова функции:
// Явное указание типов T1 и T2
std::cout << sum<double, int>(10.5, 20) << std::endl; // 30.5
// Автоматическое выведение типа
std::cout << sum(10.5, 20) << std::endl; // 30.5
Обобщенные и реальные типы можно смешивать в объявлении параметров шаблонной функции, кроме того шаблонные функции допускается перегружать. Существуют два способа перегрузки. Первый способ заключается в определении обычной функции с конкретными параметрами. Определение функции во втором случае выглядит так:
template<>
Тип Название_функции<Тип1, ..., ТипN>(Тип1 Название_переменной1
[, ..., ТипN Название_переменнойN])
{
Тело_функции
[return[ Возвращаемое_значение];]
}
После ключевого слова template
указываются пустые угловые скобки, а после названия функции внутри угловых скобок через запятую перечисляются реальные типы данных. Пример перегрузки шаблонных функций приведен в листинге 12.19.
Листинг 12.19. Перегрузка шаблонных функций
#include <iostream>
template<typename T1, typename T2>
T1 sum(T1 x, T2 y);
int sum(int x, int y);
template<>
double sum<double, double>(double x, double y);
template<typename T1, typename T2>
T1 sum(T1 x, T2 y, int z);
template<typename T, int y>
T sum(T x);
int main() {
std::cout << sum(10, 20) << std::endl; // 30
std::cout << sum(10.5, 20.4) << std::endl; // 30.9
std::cout << sum(10.5f, 20.7f) << std::endl; // 31.2
std::cout << sum(10.5, 20) << std::endl; // 30.5
std::cout << sum(10.5, 2.3, 10) << std::endl; // 22.8
// Передача значения через список обобщенных типов
std::cout << sum<int, 20>(10) << std::endl; // 30
return 0;
}
template<typename T1, typename T2>
T1 sum(T1 x, T2 y) {
return x + y;
}
int sum(int x, int y) { // Способ 1
return x + y;
}
template<>
double sum<double, double>(double x, double y) { // Способ 2
return x + y;
}
// Перегрузка и смешивание обобщенных и явных типов
template<typename T1, typename T2>
T1 sum(T1 x, T2 y, int z) {
return x + y + z;
}
// Передача значения через список обобщенных типов
template<typename T, int y>
T sum(T x) {
return x + y;
}
В качестве типа возвращаемого значения мы указали обобщенный тип T1
. Если в первом параметре передать число типа double
, а во втором — число типа int
, то все будет нормально. Тип int
будет преобразован в тип double
, затем выполнится сложение чисел и результатом станет число типа double
:
std::cout << sum(10.5, 20) << std::endl; // 30.5
Если же в первом параметре передать число типа int
, а во втором — число типа double
, то результатом выполнения функции станет число типа int
. Так как тип double
не может быть преобразован в тип int
без потерь, компилятор выведет предупреждающее сообщение:
std::cout << sum(20, 10.5) << std::endl; // 30
// warning: conversion from 'double' to 'int' may change value
Чтобы тип результата выполнения шаблонной функции соответствовал типу результата вычисления выражения, следует вместо типа возвращаемого значения указать ключевое слово auto
, а после закрывающей круглой скобки добавить оператор ->
и инструкцию decltype
, после названия которой внутри круглых скобок указать выражение (это выражение может отличаться от выражения в инструкции return
):
template<typename T1, typename T2>
auto sum(T1 x, T2 y) -> decltype(x + y) {
return x + y;
}
Теперь тип результата всегда будет соответствовать типу результата вычисления выражения:
std::cout << sum(20, 10.5) << std::endl; // 30.5
Начиная со стандарта C++14, достаточно просто указать ключевое слово auto
. Тип результата будет выведен компилятором из выражения в инструкции return
:
template<typename T1, typename T2>
auto sum(T1 x, T2 y) {
return x + y;
}
При использовании ключевого слова auto
учитывается только тип. Объявления const
(константа) и ссылки отбрасываются. Если такое поведение нежелательно, то, начиная со стандарта C++14, вместо ключевого слова auto
можно указать инструкцию decltype(auto)
:
template<typename T1, typename T2>
decltype(auto) sum(T1 x, T2 y) {
return x + y;
}
auto
мы рассматривали в разд. 3.5, а инструкцию decltype
— в разд. 3.6.Реквизиты
ЮMoney (Yandex-деньги): 410011140483022
ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов