А. Александреску - Современное проектирование на C++ (1119444), страница 11
Текст из файла (страница 11)
тпт т сваг* р = забе гет'птегргес саят<спас >(т) ! Шаблонный аргумент то указывается явно, а компилятор самостоятельно выводит шаблонный аргумент ггов, анализируя тип переменной 1. Сравнивая размеры, можно убедиться, что тип, к которому выполняется приведение, способен хранить все биты величины приводимого типа. Таким образом, указанный выше код либо выполняет вроде бы правильное приведение типов', либо проверяет диагностическое утверждение в ходе выполнения программы.
Очевидно, было бы гораздо удобнее обнаруживать такие ошибки на этапе компиляции. Прежде всего, приведение типов может находиться в очень редко используемой ветви программы. При переносе приложения на новый компилятор или платформу невозможно запомнить все машинно-зависимые части программы, поэтому в приложении могут остаться скрытые ошибки, которые приведуг к ее краху прямо на глазах у вашего заказчика. Однако все не так безнадежно. Выражения, подлежащие вычислению, являются статическими константами (сотпр)(е-типе сопзтап!).
Это означает, что их проверку можно осуществлять на этапе компиляции, а не в ходе выполнения программы. Идея этого подхода заключается в следующем. Компилятору передается языковая конструкция, являющаяся допустимой, если значение выражения не равно нулю, и недопустимой в противном случае. Таким образом, получив выражение, значение которого равно нулю, компилятор выдаст сообщение об ошибке. Простейшее решение задачи статической проверки диагностических утверждений (тап Нотп, )997) одинаково хорошо работает как в языке С, так и в языке С++, и основано на том, что массивы нулевой длины запрещены. Фт(ебзпе бтдттс снбск(ехрг) ( с)заг цппавет(((ехрг) ? 1: 0]; Теперь напишем следующий фрагмент.
тевр1асе <с1азз то, с1аяз ггов> то забе гетптегргес сазе(ггов тгов) ' В большинстве компьютеров, т.е., используя функцию гет псе гргет сазе, никогда нельзя быть уверенным и успехе. Часть !. Методы етаттс снеси(з1геоУ(ргов) < з1геоУ(то)); гетцгп ге1птегргет сваг<то>(1гов); чо16» зовеРо(птег = ...; сЬаг с за№е ге(птегргет сазт<сЬаг>(зоверо(птег); Если размер системного указателя превышает размер символа, компилятор выдаст сообщение, что программа пытается создать массив нулевой длины.
С этим подхолом связана одна проблема — сообщение об ошибке, выдаваемое компилятором, малоинформативно. "Невозможно созлать массив нулевой длины'* совсем не означает, что "Тип сЬаг слишком узок для указателя". Сделать сообщения об ошибках настраиваемыми и платформно-независимыми очень трудно. Для сообщений об ошибках не существует правил. Все они характерны лишь для данного компилятора. Например, если ошибка относится к неопределенной переменной, имя этой переменной в сообщении может не указываться. Гораздо лучше положиться на шаблоны, имеющие информативные имена.
К счастью, компилятор обязан указать имя такого шаблона в сообщении об ошибке. тевр1ате<Ьоо1> зтгисг совр11ет1вееггог; тевр1ате<> зтгост совр11ет(вееггог<тгие> (); №бег1пе етаттс снеск(ехрг) 1 (Совр11ет1вееггог<(ехрг) ! О>0) Структура Совр(1ет1веЕггог является шаблоном, получающим параметр, не являющийся типом (булевскую константу).
Она определена только лля значения тгие этой булевской константы. Если попьпаться конкретизировать шаблон с помощью выражения Совр11ет1вевггог<Уа1зе>, компилятор выдаст примерно такое сообщение: "Неопределенная специализация Совр)1ет1веЕггог<1а1зе>*'. Зто сообщение немного содержательнее предыдущего и говорит о том, что ошибка сделана преднамеренно. Разумеется, здесь есть простор для совершенствования. Можно ли настраивать сообщения об ошибках? Идея состоит в следующем.
Нужно передать дополнительный параметр в шаблон ЕтаттС СнеСк и каким-то образом сделать так, чтобы он появился в сообщении об ошибке. К недостаткам этого приема следует отнести тот факт, что настраиваемое сообщение об ошибке должно подчиняться правилам образования идентификаторов в языке С++ (не содержать пробелов, не начинаться с цифры и т.д.). Это приводит к улучшенной версии шаблона совр11ет)вееггог, показанной ниже.
Фактически шаблон совр11ет(вееггог в новом контексте становится лылопонятным. Более осмысленным является шаблон совр11ет(весЬескег, тевр1ате<Ьоо1> зтгцсг совр11ет1весЬескег; ( Совр11ет1весЬескег(...); тевр1ате<> зтгцсг совр11ет1весйескег<га1зе> (); №дет1пЕ Етдттс СНЕСК(ЕХРГ, В59) ~ й с1азз евйоя №№вз9 (); ~ (чо1б)з1аеог(совр11ет1весЬескег<(ехрг) ! О> ((еяяок №№вз90))):~ Предположим, что з1хеог(сЬаг) < з1хеог(чо10*). (Стандарт языка не гарантирует, что это выражение обязательно является истинным.) Посмотрим, что произойдет, если написать следующий фрагмент программы. Глава 2.
Приемы программирования тевр1ате <с1азз то, с1ава бгов> то заФе ге)птегргет саят(ягов бгов) ( ятлттс сняск(зз зео1(сгов) = зз зеоб(то), Оезт!пат)оп туре тоо маггов); гегигп ге!птегргет саят<то>(агав); чо!д" зовеяотптег = ...; с!заг с = заУе ге1птегргет сазт<спаг>(зовеяозптег); После обработки макроса препроцсссором код функции заФе гезптегргег саят примет слсдуюшнй вид.
тевр1ате <с1авв то, с1азз бгов> то заРе ге!птегргес саят(ягов асов) ( ( с1аьв яйлой оезт!пас!оп туре тоо маггов (); (чо!д)зззео(( совр!1ет!веспес!сег<(з!зеоУ(ягов)) <= ззгеоФ(то))>( айвой Оезт!пас!оп туре тоо маггов О)); гетигп ге)птегргет саят<то>(1гов); Этот код опрелеляет локальный класс (!оса! с!азз! яйлой Оезт!патзоп туре тоо маггов, имеюший пустое тело.
Затем он создает временное значение типа Совр11ет!веспескег<ззаеоУ(ягов) <= з!хесе(то))>, инициализированное временным значением типа айвой оезтз лат!оп туре тоо маггов. В заключение функция з!хесе вычисляет размер возникшей временной переменной. А теперь сделаем фокус! Специализация Совр!1етзвеС!зескег<тгие> содержит конструктор, нс имсюший аргументов. Это значит, что если выражение, проверяемое на этапе компиляции, имеет значение тгие, то полученная в результате программа считается правильной, в противном случае возникает ошибка компиляции. Компилятор нс может выполнить преобразование из типа елдой Оезтзпат)оп туре тоо маггов в тип совр11ет!весиескег<1а1зе>.
Приятнее всего, что теперь компилятор выводит приблизительно такое сообшение об ошибке: "Ошибка: невозможно преобразовать тип Яййой Оезт!пат!оп туре тоо маггов в тип Совр!1ет!веспескег<Фа1зе>". Есть! 2.2. Частичная специализация шаблонов Частичная специализация шаблонов позволяет спепиализировать шаблонный класс с помошькз подмножества его возможных параметров конкретизации. Рассмотрим шаблонный класс изыдет.
тевр)ате < с1ава кбпдов, с1авв соптго11ег> с)авв и(бдет обобщенная реализация Применим полную явную специализацию шаблона. тевр1абе <> с1авв и!ддет <мода1О1а1од, муСоптго11ег> Часть !. Методы специализированная реализация Классы мода1п)а1од и мусоптго11ег определяются в прикладной программе, Обнаружив определение специализации класса и)авдее, при определении объектов типа Фддет<мода1Иа!од, мусоптго11ег> компилятор будет применять специализированную, а в остальных случаях — обобщенную реализацию. Олнако иногда возникает необходимость специализировать класс и)ддет для любыт конкретизаций класса и)одоп и отдельной конкретизации класса муСоптго11ег. Вот как выглядит частичная специализация шаблона в этом случае.
// частичная специализация шаблонного класса ы)ддет тевр1ате <с1азз Мпдои> ... частичная специализированная реализация Обычно в частичной специализации шаблонного класса указываются лишь некоторые шаблонные аргументы, а остальные остаются обобщенными (депепс). Конкретизируя шаблонный класс в программе, компилятор пытается найти оптимальное соответствие. Сложный и точный алгоритм поиска соответствий позволяет программисту каждый раз по-новому осуществлять частичную специализацию. Допустим, что шабяонный класс вцттоп получает один шаблонный параметр.
Затем, даже если класс Мддет специализируется для любых конкретизаций класса Мпдоп и отдельной конкретизации класса мусоптго11ег, класс Ыодес можно и дальше частично специализировать для всех конкретизаций класса вцттоп и конкретизации мусоптго11ег. тевр1ате <с1азз вцттопдгд> с1азэ и)ддет<вцттоп<вцттопдгд>, мусоптго11ег> Как видим, возможности частичной шаблонной специализации поразительны.
Конкретизируя шаблон, компилятор выполняет сопоставление с эшелоном (рапепз гпа1с)йпд) существующих частичных и полных специализаций, подыскивая наилучший вариант. Это обеспечивает программисту невероятную свободу действий. К сожалению, частичную шаблонную специализацию невозможно применить к функциям, независимо от того, являются они членами класса или нет. Это несколько снижает уровень гибкости и степень детализации программы. ° Несмотря на то что в шаблонном классе можно выполнять полную шабйонн»ю специализацию функций-членов, к ним нельзя применять частичную шаблонную специализацию.