Анимация
JavaScript
|
Главная Библионтека 1. Что подразумевается под моделью включения для шаблонов? В модели включения (inclusion model) код шаблона с точки зрения исходного текста выглядит так же, как и встраиваемый (inline) (хотя в действительности код шаблона не обязательно должен быть встраиваемым). Весь исходный текст шаблона должен быть видимым для любого кода, который использует этот шаблон. Такая модель называется моделью включения, поскольку обычно при этом для включения заголовочных файлов с определениями шаблонов используются директивы #include". Если вы знакомы с современными шаблонами С++, то вы знакомы и с моделью включения. Это единственная реально используемая модель, хотя бы просто потому, что в настоящее время компиляторы С++ поддерживают только ее. Все шаблоны, с которыми вы сталкивались в реальных программах, книгах и статьях до этого времени, относятся к категории модели включения. 2. Что подразумевается под моделью разделения для шаблонов? С другой стороны, модель разделения (separation model) предназначается для того, чтобы позволить "разделить" компиляцию шаблонов (кавычки в данном случае использованы не случайно). В модели разделения определения шаблонов не обязательно должны быть видимы для вызывающих функций. Так и хочется добавить -• "как и определения обычных функций", но это было бы не верно - несмотря на определенную схожесть, это принципиалыю разные вещи, как .мы вскоре убедимся. Модель разделения относительно нова -- она была добавлена в стандарт в средине 1990-х годов, но первая коммерческая реализация (EDG) появилась только летом 2002 года". Самое главное при рассмотрении моделей включения и разделения - это понимать и помнить, что это разные модели организации исходного текста программы. Это не разные модели инстанцирования шаблонов, т.е. в любом случае компилятор выполняет одну и ту же работу по настройке шаблонов для конкретных аргументов. Это важно, поскольку именно в этом заключаются причины определенных ограничений экспорта (которые зачастую оказываются неожиданными для профаммистов, в особенности тех, кто прибегает к экспорту, полагая, что это позволит ускорить процесс сборки профаммы - как в случае раздельной компиляции обычных функций). При использовании любой модели компилятор вправе выполнить оптимизацию, в частности, основанную на правиле одного определения (One Definition Rule - OCR), инстанцируя шаблоны для каждой уникальной комбинации аргументов шаблонов только по одному разу, независимо от того, сколько раз и где эта комбинация встречается в вашей профаммс. Разработчики компиляторов имеют возможность реализовать такую оптимизацию и стратегию инстанцирования независимо от того, какая именно модель - включения или разделения - используется для физической организации исходных текстов шаблонов. Хотя для модели разделения возможность такого рода оптимизации очевидна, то же самое можно осуществить и при использовании модели включения. Пояснение на примере 3. В чем заключаются основные недостатки модели включения: а) для обычных функций? б) для шаблонов? " Или, что по сути то же самое, определения размещаются в отдельном . срр-файле. который в свою очередь включается в заголовочный . h-файл. Заметим, что Cfront имел похожую функциональность за десятилетие до этого. Однако реализация Cfront была медленной и основывалась на эвристике, которая при проблемах, связанных с шаблонами, просто сбрасывала кэш готовых настроенных шаблонов и начинала ин-станцирование всех шаблонов с нуля. Рассмотрим пример кода шаблона функции как в модели включения, так и в модели разделения. Для сравнения я также привожу обычную функцию - как встраиваемую и в обычном варианте раздельной компиляции; это поможет нам понять отличия между раздельной компиляцией обычных функций и раздельной моделью шаблонов функций. Это совершенно разные вещи, несмотря на то, что в их названиях использовано одно и то же слово "раздельный". Именно по этой причине я брал слово "раздельный" в кавычки. Рассмотрим следующий код с обычной встраиваемой функцией и шаблоном функции в модели включения. Пример 9-3(а): встраиваемая функция - Файл f.h, предоставляемый пользователю - namespace MyLib { inline void f( int ) { изящный код, воплотивший в себя годы работы; использует вспомогательные классы и функции Ниже приведена демонстрация использования модели включения для шаблонов: Пример 9-3(6): простенький шаблон, использующий модель включения - Файл g.h, предоставляемый пользователю - namespace MyLib { tempiate<typename т> void gC Т& ) { Изящный код - результат многих лет работы; использует вспомогательные классы и функции, которые не обязательно объявлены как встраиваемые, но их код располагается здесь же, в этом же файле В обоих случаях код примера 9-3 вызывает вопросы, знакомые для профаммистов иа С++. • Открытие исходного текста определений. Теперь определения функций f и g (которые, возможно, представляют предмет патентного права или имеют другие причины быть скрытыми) становятся доступны любому программисту. Само по себе это может и не быть такой уж большой неприятностью, но об этом несколько позже. • Зависимости исходного текста. Все функции, вызывающие f или д, зависят от деталей их внутреннего устройства, так что при любом внесении изменений в f или д требуется перекомпиляция всего кода, который к ним обращается. Кроме того, если в теле f или g используются типы, которые не упоминаются в объявлениях этих функций, то в результате вызывающие их функции также требуют доступа к полным определениям этих типов. Использование экспорта В состоянии ли мы решить (или хотя бы уменьшить) эти проблемы? 4. Как можно преодолеть недостатки из вопроса 3 при помощи стандартной модели разделения С++: а) для обычных функций? Для обычной функции можно ответить "легко", так как оба недостатка преодолеваются путем применения раздельной компиляции. пример 9-4Са): раздельная компиляция функции - Файл f.h, предоставляемый пользователю - namespace MyLib { void f( int ); - Файл f.cpp, может не быть предоставлен - namespace MyLib { void f( int ) { Изящный код - результат многих лет работы; использует вспомогательные классы и функции; компилируется отдельно } Нет ничего неожиданного в том, что данный подход решает обе проблемы, как минимум, для функций. (Та же идея может быть применена и для целых классов - о применении идиомы Pimpl вы можете прочесть в книге [SutterOO].) • Исходный текст определений скрыт. Мы можем поставлять пользователям исходный текст определений, если хотим этого, но мы не обязаны это делать. Заметим, что многие популярные библиотеки поставляются с исходным текстом (возможно, за отдельную плату), причем так поступают даже производители, которые строго следят за своими имущественными правами. Исходные тексты могут потребоваться пользователям, например, для отладочных целей или по каким-либо иным причинам. • Отсутствие зависимостей исходного кода. Вызывающая функция больше ие зависит от деталей внутреннего устройства функции f, так что при с с изменении не требуется полная перекомпиляция, достаточно перекомпоновки, что зачастую на порядок (а то и более) быстрее. Кроме того (что, правда, обычно не столь сильно влияет на время сборки приложения), вызывающая f функция больше не зависит от типов, использованных только в теле функции f. Все это хорошо известно и хорошо работает для функций. Программисты знакомы с этим методом еще со времен С, и даже еше раньше, т.е. уже много-много лет... К настоящему же вопросу мы только подбираемся...Итак, а как обстоят дела б) для шаблонов? Идея, лежащая в основе экспортирования, заключается в том, чтобы получить нечто аналогичное, но для шаблонов. Некоторые могут наивно ожидать, что приведенный ниже код обладает теми же достоинствами, что и код из примера 9-4(а). Это абсолютно неоправданно. Но не расстраивайтесь, многие знатоки С++- заблуждаются точно так же. Пример 9-4(6): экспорт шаблона - Файл g.h, предоставляемый пользователю - namespace MyLib { export tempiate<typename т> void g( T& ); - Файл g.cpp, ?? предоставляемый пользователю?? - namespace MyLib { tempiate<typename T> void g( T& ) { изящный код - результат многих лет работы; использует вспомогательные классы и функции. 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 |