ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4318
Скачиваний: 28
Глава 18. Шаблоны функций
251
ся только один раз, но где и когда это происходит — решает ком-
пилятор.
В модели компиляции с включением программист может и
явно
определить момент
конкретизации
шаблона функции. Для этого
используется ключевое слово
template
, после которого идет объ-
явление шаблона функции, в котором явно указаны аргументы
шаблона и параметры функции. Естественно, что явная конкрети-
зация шаблона функции выполняется в исходном файле.
В листингах 18.8 и 18.9 приведен пример явной конкретизации
шаблонной функции
xchange
. Заметим, что в этих листингах при-
ведены два исходных файла, принадлежащих одному проекту.
Листинг 18.8. Исходный файл с явной конкретизацией
шаблона функции
template <class T> inline void xchange(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
// явные конкретизации шаблона функции xchange
template void xchange<int>(int &a, int &b);
template void xchange<double>(double &a, double &b);
Листинг 18.9. Исходный файл, использующий
конкретизированные шаблонные функции
#include <iostream>
using namespace std;
void xchange(int &a, int &b);
void xchange(double &a, double &b);
int main()
{
int n = 10, m = 20;
xchange(n, m); // вызов шаблонной функции
cout << n << ' ' << m << endl; // печатает 20 10
Часть II. Язык программирования С++
252
double a = 1.5, b = 2.5;
xchange(a, b); // вызов шаблонной функции
cout << a << ' ' << b << endl; // печатает 2.5 1.5
return 0;
}
Заметим, что, хотя шаблонные функции и содержат в угловых
скобках аргументы шаблона, при объявлении прототипов функ-
ций нужно использовать их обычные имена.
В модели компиляции с разделением объявление шаблона функ-
ции помещаются в заголовочный файл, а определение — в ис-
ходный файл. Причем определению шаблона должно предшест-
вовать ключевое слово
export
. В этом случае шаблон называется
экспортируемым
. Шаблон функции должен быть определен как
экспортируемый только один раз во всей программе. После этого
заголовочный файл с объявлением шаблона включается во все
исходные файлы, в которых требуется конкретизация шаблона.
Такая компиляция шаблонов функций напоминает компиляцию
не
inline
функций.
Отметим, что эта модель компиляции с разделением редко под-
держивается компиляторами по причине сложности ее реали-
зации.
18.6. Перегрузка шаблонов функций
Шаблон функции может быть перегружен, т. е. допускается оп-
ределение нескольких шаблонов функций с одинаковыми имена-
ми функций, но разными сигнатурами.
Из того, что перегруженные шаблоны функций успешно объяв-
лены, не следует, что они могут быть успешно конкретизирова-
ны. Это происходит потому, что из параметров функции могут
быть выведены аргументы нескольких перегруженных шаблонов
функций. В этом случае компилятор может и не выбрать шаблон
для конкретизации функции.
В листинге 18.10 приведен пример неоднозначного выбора пере-
груженных шаблонов функции.
Глава 18. Шаблоны функций
253
Листинг 18.10. Перегрузка шаблонов функций
#include <iostream>
using namespace std;
template <class S> S sum(S a, S b) { return a + b; }
template <class S, class T> S sum(S a, T b) { return a + b; }
int main()
{
cout << sum(1.1, 2) << endl; // конкретизация второго
// шаблона функции
// cout << sum(1, 2) << endl; // ошибка, неоднозначный
// выбор шаблона функции
return 0;
}
В случае, рассмотренном в листинге 18.10, лучше оставить вто-
рой шаблон функции
sum
с двумя параметрами разных типов, т. к.
он перекрывает по специализациям первый шаблон.
Если компилятор из параметров функции выводит аргументы не-
скольких перегруженных шаблонов функций, то он выбирает тот
шаблон функции, который более специализирован по сравнению
с остальными шаблонами функций. Один из перегруженных
шаблонов функции называется более
специализированным
, чем
другой, если он допускает менее широкое множество списков
аргументов по сравнению с другим шаблоном. Другими словами,
один перегруженный шаблон функции является более специали-
зированным, чем другой перегруженный шаблон функции, если
любой список аргументов подходящий для первого перегружен-
ного шаблона функции также подходит и для второго перегру-
женного шаблона функции, но не наоборот.
Например, следующие шаблоны одноименных функций упорядо-
чены в порядке возрастания их специализации:
template<class T> void f(T);
template<class T> void f(const T);
Часть II. Язык программирования С++
254
и:
template<class T> void g(T);
template<class T> void g(T*);
template<class T> void g(const T*);
Специализация перегруженных шаблонов функций вводит на
этих шаблонах частичный порядок. Поэтому процесс выбора пе-
регруженного шаблона функции называется
частичным упорядо-
чиванием
перегруженных шаблонов.
В листинге 18.11 приведен пример выбора компилятором более
специализированного из перегруженных шаблонов функций для
конкретизации.
Листинг 18.11. Конкретизация более специализированного
шаблона функции
#include <iostream>
using namespace std;
// шаблон функции для вычисления суммы двух чисел
template <class T> T sum(T m, int n) { return m + n; }
// шаблон функции для вычисления суммы элементов массива
template <class T> T sum(T* a, int size)
{
T s = 0;
for (int i = 0; i < size; ++i)
s += a[i];
return s;
}
int main()
{
cout << sum(1, 2) << endl; // печатает 3
int a[] = {1, 2, 3};
cout << sum(a, 3) << endl; // печатает 6
return 0;
}
В случае
sum(a, 3)
конкретизируется шаблон функции для вы-
числения суммы элементов массива, т. к. он более специализиро-
Глава 18. Шаблоны функций
255
ван, т. е. первым аргументом этого шаблона может быть только
указатель. Шаблон же функции для вычисления суммы двух чи-
сел может принимать в качестве первого аргумента как указатель
на тип, так и сам тип.
Заметим, что частичное упорядочивание шаблонов функций под-
держивают не все компиляторы.
18.7. Шаблоны функций
как члены класса
Шаблон функции может быть членом класса. Объявление шабло-
на функции членом класса обозначает, что класс может содер-
жать бесконечно много различных функций-членов, которые яв-
ляются специализациями этого шаблона. Для шаблонов функций-
членов класса действуют те же правила доступа, что и для других
членов класса.
В листинге 18.12 приведен пример определения шаблона функ-
ции как члена класса.
Листинг 18.12. Шаблон функции как член класса
#include <iostream>
using namespace std;
class Demo
{
public:
template <class T> T foo(T &t) { return t; }
};
int main()
{
Demo d;
cout << d.foo(10) << endl; // печатает 10
return 0;
}