|
Программирование >> Разработка устойчивых систем
tempiate<class Т. size t N> tempiate<class Left, class Right> MyVector<T.N>& MyVector<T.N>:: operator=(const My\/ectorSum<T,N.Left.Right>& right) { for (size t i = 0: i < N: ++i) clata[i] = rightCi]: return *this: Operator+ просто сохраняет ссылки tempiate<class T. size t N> inline MyVectorSum<T.N.MyVector<T.N>,MyVector<T.N> > operator+(const MyVector<T.N>& left. const MyVector<T.N>& right) { return MyVectorSum<T.N,MyVector<T.N>.MyVector<T.N> = (left.right): tempiate<class T. si2e t N. class Left, class Right> inline MyVectorSum<T. N. MyVectorSum<T.N.Left.Right>. My\/ector<T.N> > operator+(const My\/ectorSum<T,N.Left.Right>& left, const MyVector<T.N>& right) { return MyVectorSum<T.N.My\/ectorSum<T.N.Left.Right>, MyVector<T.N> > (left, right): Вспомогательные функции для тестовой программы tempiate<class Т. size t N> void init(MyVector<T.N>& v) { for (size t i = 0; i < N: ++i) v[i] = randO % 100: tempiate<class T. size t N> void print(MyVector<T.N>& v) { for (size t i = 0: i < N; ++i) cout v[i] : cout endl: int mainO { srand(time(0)): MyVector<int. 5> vl: init(vl): print(vl): MyVector<int. 5> v2: init(v2): print(v2): MyVector<int, 5> v3: v3 = vl + v2: print(v3): Теперь поддерживается: MyVector<int. 5> v4; v4 = vl + v2 + v3; print(v4): MyVector<int, 5> v5: v5 = vl + v2 + v3 + v4: print(v5): } /:- Шаблон определяет типы операндов по аргументам Left и Right вместо того, чтобы фиксировать их заранее. Шаблон MyVectorSum получает эти два дополнительных параметра, что позволяет представить сумму произвольной комбинации пар MyVector и MyVectorSum. Оператор присваивания теперь оформлен в виде шаблона функции класса. В результате любая пара <T,N> может комбинироваться с любой парой <Left,Right>, поэтому объекту MyVector может быть присвоен объект MyVectorSum, содержащий ссылки на любую возможную пару типов MyVector и MyVectorSum. Как и в предыдущем примере, давайте проследим за ходом присваивания, начиная с выражения v4 = vl + v2 + v3; Поскольку итоговые выражения получаются весьма громоздкими, в дальнейших пояснениях мы сокращаем MyVectorSum до MVS и не приводим аргументы шаблонов. Сначала выполняется операция vl + v2, для которой вызывается подставляемая функция operator+(). В свою очередь, она вставляет выражение MVS(vl,v2) в поток компиляции. Результат прибавляется к v3, что приводит к созданию временного объекта в соответствии с выражением MVS(MVS(vl,v2),v3). В окончательном виде вся команда выглядит так: v4.operator+(MVS(MVS(vl. v2), v3)); Все эти действия выполняются компилятором. Это объясняет, почему за этой методикой закрепилось название шаблоны выражений . Шаблон MyVectorSum представляет выражение (сумму в данном случае), а вложенные вызовы напоминают дерево разбора левоассоциативного выражения vl + v2 +v3. Модели компиляции шаблонов Вероятно, вы заметили, что во всех наших примерах шаблонов в каждую единицу трансляции включаются полные определения шаблонов (например, мы размещаем их полностью в программах из одного файла или в заголовочных файлах в программах из нескольких файлов). Это противоречит общепринятой практике отделения определений обычных функций от их объявлений, когда объявления размещаются в заголовочных файлах, а реализация - в срр-файлах. Для традиционных функций такое разделение объясняется следующими причинами: присутствие тел функций, не являющихся подставляемыми, в заголовочных файлах приводит к повторному определению функций и ошибкам компоновки; скрытие реализации от клиента ослабляет привязку на стадии компиляции; фирмы-разработчики могут распространять заранее откомпилированный код (для конкретного компилятора) с заголовками, чтобы пользователи не видели реализацию функций; заголовочные файлы имеют меньшие размеры, что сокращает время компиляции. Модель с включением с другой стороны, шаблон представляет собой не фрагмент программного кода как таковой, а лишь инструкции для построения кода. Только специализации шаблонов содержат настоящий код. Если компилятор видит полное определение шаблона во время компиляции, а затем встречает специализацию этого шаблона в этой же единице трансляции, ему приходится учитывать тот факт, что идентичная специализация может присутствовать и в другой единице трансляции. Чаще всего компилятор генерирует код для всех специализаций во всех единицах трансляции, после чего компоновщик уничтожает дубликаты. Данное решение хорошо работает и для функций, которые объявлены как подставляемые, но подставляться не могут, и для таблиц виртуальных функций, чем отчасти объясняется его популярность. Впрочем, некоторые компиляторы предпочитают задействовать более сложные схемы, предотвращающие многократное генерирование одинаковых специализаций. Так или иначе, система трансляции С++ должна предотвратить ошибки, возникающие из-за появления одинаковых специализаций. Однако у такого подхода имеются недостатки: весь исходный код шаблонов виден клиенту, и у разработчика библиотек практически нет возможности скрыть свою стратегию реализации. Кроме того, заголовочные файлы имеют гораздо большие размеры, чем при раздельной компиляции тел функций. Это может привести к серьезному замедлению компиляции по сравнению с традиционными моделями. Для уменьшения размеров заголовочных файлов, необходимых для модели с полным включением, в С++ предусмотрены два альтернативных механизма организации кода (причем эти механизмы не являются взаимоисключающими). Вы можете вручную объявить все необходимые специализации, прибегнув к явной специализации, или воспользоваться экспортированными шаблонами. Явная специализация Вы можете прямо приказать компилятору сгенерировать любые указанные вами специализации шаблона. В этом случае для каждой специализации в программе может присутствовать только одна директива явной специализации; в противном случае возникают ошибки множественного определения, как для обычных функций с идентичными сигнатурами. Для демонстрации мы сначала (ошибочно) отделим объявление шаблона min(), приведенного в начале главы, от его определения, как это делается для обычных (не являющихся подставляемыми) функций. Следующий пример состоит из пяти файлов: OurMin.h - объявление шаблона функции min(); OurMin.cpp - определение шаблона функции min(); UseMinl.cpp - попытка использования специализации min() для int; UseMin2.cpp - попытка использования специализации min() для double; MinMain.cpp - вызовы useminl() и usemin2(). : C05:OurMin.h fifndef OURMIN H fdefine OURMIN H Объявление m1n() tempiate<typename T> const T& min(const T&. const T&):
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |