Программирование >>  Разработка устойчивых систем 

1 ... 59 60 61 [ 62 ] 63 64 65 ... 196


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

Приоритеты шаблонов функций

Как уже упоминалось выше, компилятор отдает предпочтение перегрузке обычной функции min() перед использованием шаблона. Если уже имеется функция, соответствующая вызову, зачем генерировать другую? Однако при отсутствии обычных функций перегруженные шаблоны функций могут привести к неоднозначности. Чтобы вероятность этого была сведена к минимуму, для шаблонов функций определен приоритет, при котором выбирается наиболее специализированный шаблон. Шаблон функции считается более специализированным, чем другой шаблон, если каждый потенциально подходящий для него список аргументов также подходит и для другого шаблона, но не наоборот. Рассмотрим следующие объявления шаблонов функций, взятые из примеров стандарта С++:

tempiate<class Т> void f(T): template<class T> void f(T*): template<class T> void f(const T*);

Для первого шаблона подходит любой тип. Второй шаблон более специализирован, чем первый, потому что для него подходят только типы-указатели. Иначе говоря, набор вызовов, подходящих для второго шаблона, составляет подмножество набора вызовов для первого шаблона. Аналогичные отношения существуют между вторым и третьим объявлениями: третий шаблон может вызываться только для указателей на const, тогда как второй шаблон допускает любые типы указателей. Следующая программа показывает, как работают эти правила:

: C05:PartialOrder.cpp

Упорядочение шаблонов функций

linclude <iostream>

using namespace std:

tempiate<class T> void f(T) { cout T endl:

tempiate<class T> void f(T*) { cout T* endl:

tempiate<class T> void f(const T*) { cout const T* endl:

int mainO { f(0): T

int i = 0:



f(&i): Т*

const int j - 0; f(&j): const T*

} III:-

Бесспорно, вызов f(&i) соответствует первому шаблону, но поскольку второй шаблон более специализирован, вызывается именно он. Вызов третьего шаблона невозможен, поскольку указатель не является указателем на const. Вызов f (&j) подходит для всех трех шаблонов (например, во втором шаблоне Т будет соответствовать const int), но третий шаблон снова выбирается как наиболее специализированный.

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

Специализация шаблонов

Термин специализация в С++ имеет конкретный смысл, связанный с шаблонами. Определение шаблона по своей природе является обобщением, поскольку оно описывает семейство функций или классов со сходными характеристиками. При получении аргументов шаблон специализируется, то есть определяется уникальный экземпляр из множества возможных экземпляров, входящих в это семейство функций или классов. Шаблон функции min(), приведенный в начале главы, представляет обобщенную функцию определения минимума, поскольку типы его параметров не заданы. Но стоит задать значения параметров шаблона явно или косвенно (с вычислением типов на основании имеющихся данных), и компилятор сгенерирует специализированную версию шаблона (например, min<int>).

Явная специализация

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

Вспомните, что в приведенном примере MinTestcpp присутствовала следующая обычная функция:

const char* min(const char* a. const char* b) { return (strcmpCa. b) < 0) ? a : b:

Она была нужна для того, чтобы вызов min() сравнивал строки, а не адреса. Хотя в данном случае это не дало бы никаких преимуществ, мы также могли бы определить отдельную специализацию min() для const char*, как сделано в следующей программе:

: C05:MinTest2.cpp linclude <cstring> linclude <iostream> using std::strcmp:



using std::cout: using std::endl;

tempiate<class T> const T& min(const T& a. const T& b) { return (a < b) ? a : b:

Переопределение специализации шаблона min tempiate<>

const char* const& m1n<const char*>(const char* const& a.

const char* const& b) {

return (strcmpCa. b) < 0) ? a : b:

int mainO {

const char *s2 - say \ Ni-!\ . *sl - knights who : cout minCsl. s2) endl: cout mino(sl. s2) endl: } III:-

Префикс templateo сообщает компилятору, что далее следует специализация шаблона. Тип специализации должен быть указан в угловых скобках сразу же после имени функции, как при обычном вызове. Обратите внимание на то, как тщательно в явной специализации параметр Т заменяется на const char*. Всюду, где в исходном шаблоне присутствует const Т, const модифицирует весь тип Т. В результате получается константный указатель const char*. Следовательно, в специализации вместо const Т должна использоваться запись const char* const. Когда компилятор встречает вызов min() с аргументами const char*, он создает экземпляр нашей версии ппп() для const char*, чтобы она могла быть вызвана в программе. Для вызова min() в приведенном примере требуется та же специализация min().

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

В стандартную библиотеку входит переопределенная специализация шаблона vector для хранения объектов типа bool. Предполагается, что vector<bool> позволит реализациям библиотеки сэкономить память за счет упаковки битов в целых чис-лах.

Как было показано ранее, объявление основного шаблона класса vector выглядит так:

template <class Т. class Allocator - allocator<T> > class vector {...}:

Объявление переопределенной специализации для объектов типа bool могло бы выглядеть так:

templateo class vector<bool, allocator<bool> > {...}

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

Контейнер vector<bool> подробно рассматривается в главе 7.



1 ... 59 60 61 [ 62 ] 63 64 65 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика