Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 [ 11 ] 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

STATic CHECKCsizeofCFroni) <= sizeofCTo)); return reinterpret cast<To>Cfrom);

void* somepointer = ...;

char с = safe reinterpret cast<char>CsomePointer);

Если размер системного указателя превышает размер символа, компилятор выдаст сообщение, что профамма пытается создать массив нулевой длины.

С этим подходом связана одна проблема - сообщение об ошибке, выдаваемое компилятором, малоинформативно. "Невозможно создать массив нулевой длины" совсем не означает, что "Тип char слишком узок для указателя". Сделать сообщения об ошибках настраиваемыми и платформно-независимыми очень трудно. Для сообщений об ошибках не существует правил. Все они характерны лишь для данного компилятора. Например, если ошибка относится к неопределенной переменной, имя этой переменной в сообщении может не указываться.

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

tempiate<boo1> struct CompileTimeError; tempiateo struct Compi1eTimeError<true> {};

#define STATlC CHECKCexpr) \

CCompi1eTimeError<Cexpr) != 0>C))

Структура Compi leTimeError является шаблоном, получающим параметр, не являющийся типом (булевскую константу). Она определена только для значения true этой булевской константы. Если попытаться конкретизировать шаблон с помощью выражения Compi1eTimeError<fa1se>, компилятор выдаст примерно такое сообщение: "Неопределенная спещ1ализаиия Compi 1eTimeError<fa1se>". Это сообщение немного содержательнее предыдущего и говорит о том, что ошибка сделана преднамеренно.

Разумеется, здесь есть простор для совершенствования. Можно ли настраивать сообщения об ошибках? Идея состоит в следующем. Нужно передать дополнительный параметр в шаблон STATIC CHECK и каким-то образом сделать так, чтобы он появился в сообщении об ошибке. К недостаткам этого приема следует отнести тот факт, что настраиваемое сообщение об ошибке должно подчиняться правилам образования идентификаторов в языке С++ (не содержать пробелов, не начинаться с цифры и т.д.). Это приводит к улучшенной версии шаблона Compi leTimeError, показанной ниже. Фактически шаблон Compi leTimeError в новом контексте становится малопонятным. Более осмысленным является шаблон CompileTimeChecker.

tempiate<boo1> struct CompileTimeChecker; {

CompileTimeCheckerС...);

templateo struct Compi1eTimeChecker<fa1se> {}; #define STATlC CHECKCexpr, msg) \ {\

class ERROR ##msg {}; \

Cvoid)sizeofCCompi1eTimeChecker<Cexpr) != 0> CCERROR ##msgC))));\

Предположим, что sizeofCchar) < sizeof(void*). (Стандарт языка не гарантирует, что это выражение обязательно является истинным.) Посмотрим, что произойдет, если написать следующий фрагмент профаммы.



template <class то, class from>

To safe reinterpret cast(From from)

STATlC CHECK(si2eof(From) <= sizeofCro),

Destination Type Too Narrow); return reinterpret cast<To>(from);

void* somePointer = ...;

char с = safe reinterpret cast<char>(somePointer);

После обработки макроса препроцессором код функции safe reinterpret cast примет следующий вид.

template <class то, class from>

То safe reinterpret cast(From from)

class ERROR Destination Type Too Narrow {}; (void)sizeof( CompileTimeChecl<er<(sizeof(From)) <= sizeof(To))>( ERROR Destination Type Too Narrow ()));

return reinterpret cast<To>(from);

Этот код определяет лоА:а/7ь«ын класс (local class) ERROR Destination Type Too Narrow, имеющий пустое тело. Затем он создает временное значение типа Compi 1 eTi meChecl<er<si zeof (From) <= sizeof(To))>, инициализированное временным значением типа ERROR Destination Type Too Narrow. В заключение функция si zeof вычисляет размер возникшей временной переменной.

А теперь сделаем фокус! Специализация Compi 1 eTimeChecker<true> содержит конструктор, не имеющий аргументов. Это значит, что если выражение, проверяемое на этапе компиляции, имеет значение true, то полученная в результате профамма считается правильной, в противном случае возникает ошибка компиляции. Компилятор не может выполнить преобразование из типа ERROR Destination Type Too Narrow в тип Compi leTimeChecker<false>. Приятнее всего, что теперь компилятор выводит приблизительно такое сообщение об ошибке: "Ошибка; невозможно преобразовать тип ERROR Destination Type Too Narrow в тип compileTimeChecker<false>".

Есть!

2.2. Частичная специализация шаблонов

Частичная специализация шаблонов позволяет специализировать шаблонный класс с помощью подмножества его возможных парамефов конкретизации. Рассмотрим шаблонный класс Widget.

template < class window, class Controller>

class widget

.. обобщенная реализация . . .

Применим полную явную специализацию шаблона, template о

class widget <ModalDialog, MyController> {



.. специализированная реализация ...

Классы ModalDialog и MyController определяются в прикладной программе.

Обнаружив определение специализации класса widget, при определении объектов типа widget<ModalDialog, MyControner> компилятор будет применять специализированную, а в остальных случаях - обобщенную реализацию.

Однако иногда возникает необходимость специализировать класс widget для любых конкретизации класса Window и отдельной конкретизации класса MyController. Вот как выглядит частичная специализация щаблона в этом случае.

Частичная специализация шаблонного класса widget

template <class window>

... частичная специализированная реализация ...

Обычно в частичной специализации шаблонного класса указываются лишь некоторые шаблонные аргументы, а остальные остаются обобщенными (generic). Конкретизируя шаблонный класс в программе, компилятор пытается найти оптимальное соответствие. Сложный и точный алгоритм поиска соответствий позволяет программисту каждый раз по-новому осуществлять частичную специализацию. Допустим, что шаблонный класс Button получает один шаблонный параметр. Затем, даже если класс widget специализируется для любых конкретизации класса window и отдельной конкретизации класса MyController, класс widget можно и дальше частично специализировать для всел: конкретизации класса Button и конкретизации MyController.

template <class ButtonArg>

class widget<Button<ButtonArg>, MyController> {

Как видим, возможности частичной шаблонной специализации поразительны. Конкретизируя шаблон, компилятор выполняет сопоставление с эталоном (pattern matching) существующих частичных и полных специализаций, подыскивая наилу1ший вариант. Это обеспечивает программисту невероятную свободу действий.

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

• Несмотря на то что в шаблонном классе можно выполнять полную шаблонную специализацию функций-членов, к ним нельзя применять частичную шаблонную специализацию.

• Шаблонные функции из просфанства имен (не являющиеся членами какого-либо класса) невозможно Частично специализировать. Больше всего на частичную специализацию шаблонных функций из пространства имен (namespace-level template functions) похож процесс их замещения. С практической точки зрения это означает, что возможность детальной специализации существует лишь для параметров функций, но не для возвращаемых ими значений или используемых внутри нее типов.

template <class т, class и> т Fun(u obj); исходный шаблон template <class и> void Fun<void, и>(и obj); неправильная

частичная специализация template <class т> т Fun (window obj); правильно (перегрузка)



0 1 2 3 4 5 6 7 8 9 10 [ 11 ] 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105