Анимация
JavaScript
|
Главная Библионтека public: void DoSomethingCT* pObj) { DoSomethingCpObj, lnt2Type<isPolymorphic>()); Класс intZType оказывается очень удобным для перевода некоторого значения в тип. После преобразования временную переменную этого типа можно передавать перегруженным функциям, реализующим два требуемых алгоритма. Этот прием работает благодаря тому, что компилятор не генерирует шаблонные функции, которые никогда не вызываются, - он только проверяет их синтаксис. Таким образом, шаблонный код позволяет осуществлять диспетчеризацию во время компиляции. В библиотеке Loki класс intZType используется в нескольких местах, особенно в главе 11, посвященной мультиметодам. В этой главе шаблонный класс представляет собой механизм двойной диспетчеризации, а шаблонный булевский параметр дает возможность осуществлять симметричную диспетчеризацию. 2.5. Отображение одного типа в другой в разделе 2.2 указывалось, что частичной специализации шаблонных функций не существует. Иногда, однако, хотелось бы иметь такую возможность. Рассмотрим следующую функцию. template <class Т, class U> Т* CreateCconst U& arg) { return new T(arg); Функция Create создает новый объект, передавая аргумент его конструктору. Предположим, что в нашем приложении принято следующее правило. Объект типа Widget содержит недоступный унаследованный код, и для его создания нужны два аргумента, один из которых представляет собой фиксированное число, скажем, -1. На наш собственный класс, производный от класса Widget, такие офаничения не накладываются. Каким образом можно специализировать функцию Create так, чтобы она обрабатывала объект класса widget иначе, чем все другие классы? Очевидное решение таково: нужно создать отдельную функцию Createwidget. К сожалению, в данный момент не существует универсального интерфейса для создания объектов класса Widget и объектов, производных от этого класса. Это делает функцию Create непригодной для использования в обобщенном коде (generic code). Функцию невозможно специализировать отдельно, т.е. нельзя написать что-то вроде следующего кода. неправильный код - не пытайтесь его применять! template <class U> widget* Create<widget, u>(const U& arg) { return new widget(arg, -1); В отсутствие частичной специализации функций существует только одно средство ~ перефузка. Следовательно, для решения поставленной задачи можно передать функции Create фиктивный объект (dummy object) типа т и применить перефузку. template <c1ass T, class u> T* CreateCconst u& arg, T /* фиктивный объект */) { return new TCarg); template <c1ass u> widget* CreateCconst u& arg, widget /* фиктивный объект */) { return new widgetCarg, -1); Такое решение может повлечь за собой перефузку консфуктора сколь угодно сложного объекта, что также практически бесполезно. Нам необходимо эффективное средство для передачи информации о типе т в функцию Create. Для этого и нужен класс туре2туре. Объект этого класса представляет собой идентификатор, несуший информацию о типе. Его можно передавать перефужаемым функциям. Рассмофим определение класса Туре2туре. template <typename т> struct туре2туре { typedef т OriginalType; Класс Туре2туре предназначен для передачи любого значения, причем каждому отдельному типу соответствует своя конкретизация, что и фебовалось доказать. Теперь мы можем написать следуюший код. Реализация функции Create на основе перегрузки и класса Туре2туре template <Class т, class и> т* CreateCconst и& arg, Туре2туре<т>) return new тСагд); template <c1ass и> widget* CreateCconst u& arg, Type2Type<widget>) { return new WidgetCarg, -1); используем функцию Create String* pStr = CreateCHello", Type2Type<String>C)); Widget* pw = CreateClOO, Type2Type<widget>C)); Второй парамеф функции Create предназначен только для выбора подходящей пе-рефузки. Теперь эту функцию можно специализировать различными конкретизация-ми класса Туре2туре, которые преобразовьЕваются в разные типы, используемые в нашем приложении. 2.6. Выбор типа Иногда в обобщенном коде возникает необходимость выбрать тип в зависимости от булевской константы. Допустим, что в примере, связанном с классом NiftyContainer, который обсуждался в разделе 2.4, в качестве средства для внешнего хранения мы хотим использовать объекты класса std::vector. Очевидно, полиморфные типы можно хранить только в виде указателей, а не значений. С другой стороны, мы можем пофебовать, чтобы неполиморфные типы сохранялись в виде значений, поскольку это более эффективно. Рассмотрим шаблонный класс. template <typename т, bool isPolymorphio class NiftyContainer Потребуем, чтобы в нем хранились объекты класса vector<T*> (если значение переменной isPolymorphic равно true) либо объекты класса vector<T> (если значение переменной isPolymorphic равно false). По существу, нам нужен оператор typedef valueType, значением которого в зависимости от значения булевской переменной isPolymorphic являются типы т* или т. Для этого можно применить шаблонные классы характеристик (traits) (Alexandrescu, 2000а). template <typename т, bool isPolymorphio struct NiftyContainervalueTraits typedef T* valueType; template <typename T> struct NiftyContainervalueTraits<T, false> { typedef T ValueType; template <typename T, bool isPolymorphio class NiftyContainer typedef NiftyContainerValueTraits<T, isPolymorphio Traits; typedef typename Traits: .-valueType ValueType; Это слишком грубое решение задачи. Более того, оно не масштабируется: для каждого выбираемого типа приходится определять новый шаблонный класс характеристик. Шаблонный класс Select из библиотеки Loki позволяет выбрать тип прямо на месте. Его определение использует частичную шаблонную специализацию. template <bool flag, typename т, typename u> struct select typedef T Result; template <typename T, typename u> struct Select<false, T, u> typedef и Result; Если значение переменной flag равно true, компилятор использует первое (обобщенное) определение, и, следовательно, тип Result становится равным т. Если значение переменной flag равно false, вступает в действие специализация, и тип Result становится равным и. Теперь намного легче определить переменную NiftyContainer::ValueType. template <typename т, bool isPolymorphio class NiftyContainer 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 |