И.А. Волкова, А.В. Иванов, Л.Е. Карпов - Основы объектно-ориентированного программирования. Язык программирования С++ (1114893), страница 15
Текст из файла (страница 15)
Статическое приведение возможно также для свободных указателей(void*), которые могут преобразовываться в значения любых типов указателей, и для преобразований между арифметическими типами.101Динамическая информация о типе (RTTI).Однако при этом невозможно проверить корректность преобразованийс использованием исключительной ситуации bad_cast. Кроме того, невозможно преобразование указателя к указателю из другой ветви иерархиинаследования, основанной на том же базовом классе, даже если объект созданна основе класса, являющегося наследником классов рассмотренныхуказателей. Так, если в приведенном выше примере динамическоепреобразование позволяет преобразовать указатель типа C* к указателю B*,если объект является объектом типа класс E, то статическое преобразованиене дает возможности преобразовать указатель типа C* к указателю B*.ПримечаниеКроме динамического и статического преобразований имеются и другие преобразования.
Например, преобразование reinterpret_cast (управляет преобразованиями между произвольными несвязанными типами. Результатом является значение нового типа, состоящее из той же последовательности битов,что и аргумент преобразования). Константное (const_cast) преобразованиеаннулирует действие модификатора const (ни статическое, ни динамическоепреобразования действие модификатора const аннулировать не могут). Подробнее о преобразованиях см.
в литературе по С++, например, в [12].102Параметрический полиморфизмГлава 15.ПараметрическийполиморфизмПараметрический полиморфизм позволяет применить один и тот же алгоритм к разным типам данных. При этом тип является параметром тела алгоритма. Механизм шаблонов, реализующий параметрический полиморфизм,позволяет легче разрабатывать стандартные библиотеки.Шаблон представляет собой предварительное описание функции илитипа, конкретное представление которых зависит от параметров шаблона.Так, если необходимо написать функции нахождения суммы элементов числовых массивов разных типов (например, int, float или double), то вместотрех различных функций можно написать один шаблон.15.1. Параметры шаблонаДля описания шаблонов используется ключевое слово template, вслед за которым указываются аргументы (формальные параметры шаблона), заключенные в угловые скобки.
Формальные параметры шаблона перечисляютсячерез запятую, и могут быть как именами объектов, так и параметрическимиименами типов (встроенных или пользовательских). Параметр-тип описывается с помощью служебного слова class или служебного слова typename.В соответствии со Стандартом ISO 1998 C++ (см.
[2], раздел 14.1.4)параметром шаблона может быть:— параметрическое имя типа— параметр-шаблон— параметр одного из следующих типов: интегральный перечислимый, указатель на объект любого типа (встроенного или пользовательского) или на функцию ссылка на объект любого типа (встроенного или пользовательского) или на функцию указатель на член класса, в частности, указатель на методкласса103Параметрический полиморфизмИнтегральные типы (раздел 3.9.1 Стандарта):— знаковые и беззнаковые целые типы,— bool, char, wchar_tПримечаниеПеречислимые типы не относятся к интегральным, но их значения приводятсяк ним в результате целочисленного повышения.При использовании типа в качестве параметра перед параметром, являющимся параметрическим именем типа, необходимо использовать одно изключевых слов: либо class, либо typename.ПримечаниеНе все компиляторы обрабатывают вложенные шаблоны (например, Microsoft*Visual C++ 6*: Visual Studio 98*).
Тем не менее, шаблонный класс может использоваться в качестве значения параметра по умолчанию. Далее будетрассмотрен шаблонный класс со следующим прототипом:template <class T, class A=allocator <T> > сlass vector{ . . .};В разделе 14.1.7 Стандарта явно сказано, что нетиповый параметр неможет иметь тип void, пользовательский тип, или плавающий тип, например:// template <double d> class X{ . . . };template <double* d> class X{ . .
. };template <double& d> class X{ . . . };// ошибка// OK// OKТаким образом, параметром шаблона не может быть формальный параметр нецелочисленного типа, например, float или double. Это можно объяснить тем, что основным назначением формальных параметров в качествепараметров шаблона является определение числовых характеристик параметрических типов (например, размер вектора, стека и так далее).Вместе с тем параметром шаблона может быть указатель, в том числеи на объект нецелочисленного типа. При этом, естественно, указатель —фактический параметр может содержать как адрес одного объекта, таки массива однородных объектов.Использование шаблонов позволяет создавать универсальные алгоритмы, без привязки к конкретным типам.15.2.
Шаблоны функцийСинтаксис шаблонов функций:104Параметрический полиморфизмПример:template <class T> T sum(T array[], int size){T res = 0;int i;for (i = 0; i < size; ++i )res += array[i];return res;}При обращении к функции-шаблону после имени функции в угловыхскобках указываются фактические параметры шаблона — имена реальныхтипов или значения объектов:Для конкретного использования приведенного выше шаблона длямассивов целых чисел размерности 10 следует написать:int iarray[10];int i_sum;//...i_sum = sum <int> (iarray, 10);Встретив такую конструкцию, компилятор сгенерирует конкретнуюреализацию функции sum с учётом специфицированных параметров (такаяфункция называется порожденной функцией).Так как допускается параметризовать шаблоны не только именами типов, но и объектами, то аргумент size можно указать в виде параметра шаблона:template <class T, int size> T sum (T array[]) { /* ...
*/ }Тогда вызов sum будет выглядеть соответственно:i_sum = sum <int, 10> (iarray);105Параметрический полиморфизмЗамечаниеСледует отметить, что использование шаблонов сокращает текст программы,но не сокращает программный код. в программе реально будет сгенерированостолько порожденных функций, сколько имеется вызовов функций с разныминаборами фактических параметров шаблона.15.3. Специализация шаблоннойфункцииПо сути, при использовании функции-шаблона используется механизм, позволяющий автоматически перегружать функции компилятором. Однакофункцию-шаблон можно перегрузить и явно.Если шаблонный алгоритм является неудовлетворительным для конкретного типа аргументов или неприменим к ним, то можно описать обычную функцию, список типов аргументов и возвращаемого значения которойсоответствуют объявлению шаблона. Такая, перегружающая шаблон, функция, называется специализацией шаблонной функции.Следует отметить, что при обращении к функции-шаблону после именифункции можно и не указывать в угловых скобках фактические параметрические типы шаблона.
в этом случае компилятор автоматически определяетфактические параметрические типы шаблона по типам фактических параметров вызова функции. Это называется выведением типов аргументовшаблона. Выведение типов аргументов возможно при условии, что списокфактических параметров вызова функции однозначно идентифицирует список параметров шаблона.Так, описанную выше шаблонную функцию можно вызвать следующимобразом:int iarray[10];int i_sum;//...i_sum = sum (iarray, 10);Так как тип первого фактического параметра — int, то компилятор автоматически трактует данный вызов, как:i_sum = sum <int> (iarray, 10);Если параметр шаблона можно вывести из более, чем одного фактического параметра функции, результатом каждого выведения должен быть одини тот же тип. Иначе вызов будет ошибочным.Пример:template <class T> void f(T i, T* p) { /* .
. . */ }// . . .void g(int i){106Параметрический полиморфизм}// . . .f(i, &i); // правильный вызов// . . .// f(i,”hello world”); - ошибка, т.к. по первому// параметру T — int, по второму — const charВерсия шаблона для конкретного набора фактических параметровшаблона также называется специализацией.При выведении типов аргументов шаблона по типам фактических параметров функции нельзя применять никаких описанных выше преобразований параметров, кроме преобразований Точного отождествления, то есть:— Точное совпадение— Совпадение с точностью до typedef— Тривиальные преобразования:T[]↔T*T↔T&T→const TПример:template <class T> T max(T t1, T t2) {.
. .}int main(){max(1,2);// max<int>(1,2);max(’a’,’b’);// max<char>(’a’,’b’);max(2.7, 4.9);// max<double>(2.7, 4.9);//max(’a’,1);// ошибка — неоднозначность,// стандартные преобразования не// допускаются//max(2.5,4);// ошибка — неоднозначность,// стандартные преобразования не// допускаются}Неоднозначности не возникают при использовании явного квалификатора:max <int>(‘a’, 1);max <double>(2.5, 4);15.4. Алгоритм поиска оптимальноотождествляемой функции(с учетом шаблонов)С одним и тем же именем функции можно написать несколько шаблонови перегруженных обычных функций.
При этом одна специализация считаетсяболее специализированной, чем другая, если каждый список аргументов,107Параметрический полиморфизмсоответствующий образцу первой специализации, также соответствуети второй специализации.Алгоритм выбора перегруженной функции с учетом шаблонов являетсяобобщением правил выбора перегруженной функции.1. Для каждого шаблона, подходящего по набору формальных параметров, осуществляется формирование специализации, соответствующей списку фактических параметров.2.
Если могут быть два шаблона функции и один из них более специализирован, то на следующих этапах рассматривается только он(порядок специализаций описан далее).3. Осуществляется поиск оптимально отождествляемой функции изполученного набора функций, включая определения обычныхфункций, подходящие по количеству параметров.
При этом еслипараметры некоторого шаблона функции были определены путемвыведения по типам фактических параметров вызова функции, топри дальнейшем поиске оптимально отождествляемой функции кпараметрам данной специализации шаблона нельзя применять никаких описанных выше преобразований, кроме преобразованийТочного отождествления4. Если обычная функция и специализация подходят одинаково хорошо, то выбирается обычная функция.5.