Программирование >>  Разработка устойчивых систем 

1 ... 72 73 74 [ 75 ] 76 77 78 ... 196


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&):



1 ... 72 73 74 [ 75 ] 76 77 78 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика