Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 216 217 218 219 220 221 222 223 224 ... 297
Перейти на страницу:

16.1. Определение шаблона

Предположим, необходимо написать функцию, которая сравнивает два значения и указывает, является ли первое из них меньшим, равным или большим, чем второе. Фактически придется создать несколько таких функций, каждая из которых сможет сравнивать значения определенного типа. На первом этапе можно было бы определить несколько перегруженных функций.

// возвращает 0, если значения равны, -1, если v1 меньше, и 1,

// если меньше v2

int compare(const string &v1, const string &v2) {

 if (v1 < v2) return -1;

 if (v2 < v1) return 1;

 return 0;

}

int compare(const double &v1, const double &v2) {

 if (v1 < v2) return -1;

 if (v2 < v1) return 1;

 return 0;

}

Эти функции почти идентичны и отличаются только типом параметров. Тела у обеих функций одинаковы.

Повторение тела функции для каждого сравниваемого типа не только утомительно, но и повышает вероятность возникновения ошибок. Однако важней всего то, что в этом случае необходимо заранее знать все типы, которые придется сравнивать. Этот подход не сработает в случае, когда функцию предполагается использовать для типов, неизвестных на данный момент.

16.1.1. Шаблоны функций

Вместо того чтобы определять новую функцию для каждого типа, мы можем определить шаблон функции (function template). Шаблон функции — это проект, по которому можно создать некую версию данной функции, специфическую для заданного типа. Шаблон функции compare() может выглядеть так:

template <typename Т>

int compare(const T &v1, const T &v2) {

 if (v1 < v2) return -1;

 if (v2 < v1) return 1;

 return 0;

}

Определение шаблона начинается с ключевого слова template, за которым следует разделяемый запятыми и заключенный в угловые скобки (<>) список параметров шаблона (template parameter list), один или несколько параметров шаблона (template parameter).

Список параметров в определении шаблона не может быть пустым

Список параметров шаблона очень похож на список параметров функции. Список параметров функции задает имена и типы локальных переменных, но оставляет их неинициализированными. Инициализацию параметров во время выполнения обеспечивают аргументы.

Аналогично параметры шаблона представляют типы или значения, используемые при определении класса или функции. При использовании шаблона необходимо (явно или неявно) определить аргументы шаблона (template argument), чтобы связать их с соответствующими параметрами шаблона.

Например, рассматриваемая функция compare() объявляет единственный параметр типа Т. В шаблоне compare имя Т можно использовать там, где должно быть название типа данных. Фактический тип Т будет определен компилятором на основании способа применения функции.

Создание экземпляра шаблона функции

Когда происходит вызов шаблона функции, для вывода типов аргументов шаблона компилятор обычно использует аргументы вызова. Таким образом, когда происходит вызов шаблона compare, компилятор использует тип аргументов для определения типа, связанного с параметром шаблона Т. Рассмотрим следующий вызов:

cout << compare(1, 0) << endl; // Т - тип int

Здесь аргумент имеет тип int. Компилятор выведет и использует тип int как аргумент шаблона, а также свяжет этот аргумент с параметром Т шаблона.

При создании экземпляра (instantiation) специфической версии функции компилятор сам использует выведенные параметры шаблона. При этом он подставляет фактические аргументы шаблона вместо соответствующих параметров шаблона. Рассмотрим следующий вызов:

// создание экземпляра int compare(const int&, const int&)

cout << compare(1, 0) << endl; // T - тип int

// создание

// экземпляра int compare(const vector<int>&, const vector<int>&)

vector<int> vec1{1, 2, 3}, vec2{4, 5, 6};

cout << compare(vec1, vec2) << endl; // T - тип vector<int>

Здесь компилятор создает два экземпляра разных версий функции compare(). В первой из них параметр Т заменен типом int.

int compare(const int &v1, const int &v2) {

 if (v1 < v2) return -1;

 if (v2 < v1) return 1;

 return 0;

}

Во втором вызове создается версия функции compare() с параметром Т, замененным типом vector<int>. Такое создание компилятором функций обычно и называют созданием экземпляра шаблона.

Параметры типа шаблона

У функции compare() есть один параметр типа (type parameter) шаблона. Как правило, параметр типа можно использовать как спецификатор типа таким же образом, как и встроенный спецификатор типа или класса. В частности, параметр типа применим при назначении типа возвращаемого значения или типа параметра функции, а также в объявлениях переменных или приведениях в теле функции:

// ok: для возвращаемого значения и параметра используется тот же тип

template <typename Т> Т foo(Т* p) {

 Т tmp = *p; // тип tmp совпадает с типом, на который указывает p

 // ...

 return tmp;

}

Каждому параметру типа должно предшествовать ключевое слово class или typename:

// ошибка: U должно предшествовать либо typename, либо class

template <typename Т, U> Т calc(const T&, const U&);

В списке параметров шаблона эти ключевые слова имеют одинаковый смысл и применяются взаимозаменяемо. Оба ключевых слова применимы одновременно:

// ok: в списке параметров шаблона нет никакой разницы между ключевыми

// словами typename и class

template <typename Т, class U> calc(const T&, const U&);

Для обозначения параметра типа шаблона интуитивно понятней использовать ключевое слово typename, а не class; в конце концов, для фактического типа параметра вполне может быть использован встроенный тип, а не только класс. Кроме того, ключевое слово typename более точно указывает на то, что следующее за ним имя принадлежит типу. Однако ключевое слово typename было добавлено в язык С++ как часть стандарта С++, поэтому в устаревших программах, вероятнее всего, осталось исключительно ключевое слово class.

Параметры значения шаблона

Кроме параметров типа, в определении шаблона могут быть использованы параметры значения (nontype parameter). Параметр значения представляет значение, а не тип. При определении параметров значения вместо ключевого слова class или typename используются имена типов.

При создании экземпляра шаблона такие параметры заменяются значением, предоставленным пользователем или выведенным компилятором. Чтобы компилятор смог создать экземпляр шаблона во время компиляции, эти значения должны быть константными выражениями (см. раздел 2.4.4).

В качестве примера напишем версию функции compare(), работающую со строковыми литералами. Такие литералы представляют собой массивы типа const char. Поскольку скопировать массив нельзя, определим параметры как ссылки на массив (раздел 6.2.4). Поскольку необходима возможность сравнивать литералы разных длин, снабдим шаблон двумя параметрами значения. Первый параметр шаблона представляет размер первого массива, а второй — размер второго:

template<unsigned N, unsigned M>

int compare(const char (&p1)[N], const char (&p2)[M]) {

 return strcmp(p1, p2);

}

При вызове следующей версии функции compare() компилятор будет использовать размер литералов для создания экземпляра шаблона с размерами, которыми заменяют параметры N и M:

compare("hi", "mom")

Не забывайте, что компилятор завершает строковый литерал пустым символом (см. раздел 2.1.3). В результате компилятор создаст такой экземпляр:

int compare(const char (&p1)[3], const char (&p2)[4])

Параметр значения может быть целочисленным типом, указателем, ссылкой на объект (l-значением) или на тип функции. Аргумент, связанный с целочисленным параметром значения, должен быть константным выражением. У аргументов, привязанных к указателю или ссылочному параметру значения, должна быть статическая продолжительность существования (см. главу 12). Нельзя использовать обычный (нестатический) локальный или динамический объект как аргумент шаблона для параметра значения шаблона в виде ссылки или указателя. Параметр-указатель может быть также создан как nullptr или нулевое константное выражение.

1 ... 216 217 218 219 220 221 222 223 224 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии