Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 16
Текст из файла (страница 16)
Ниже показано„как это можно сделать в нашем простом примере. // Ьавасв/шуйагвг4.Ьрр №Тйпс)ей МгР1ЕЕТ НРР №с)еййпе МТР1ЕЕТ НРР // если определено г)Яе ехРОет, используется ехрогг №Тй с)ей1пес)(ОБЕ ЕХРОЙТ) №с)еййпе ЕХРОЕТ ехрогс №е1ие №с)еййпе ЕХРОЕТ №епс)Н // Объявление шаблона ЕХРОЕТ сешр1аге <Сурепаше Т> г Заметим, что далеко ие все считают закрытость исходного кода преимуществом Глава 6. Применение шаблонов на практике 94 чоМ рт1пс суреой (т сопвса) // включение определений, если УЯЕ ЕХРОЕТ не задано й1Т 1)йей1пес)(ОЯЕ ЕХРОЕТ) Ф1пс1пс)е "юуй1гвс.срр" йепс)1Т йепг)1Т // ИХРТЕЯТ НРР Теперь можно выбирать между двумя моделями путем определения или пропуска символа ОЯЕ ЕХРОЕТ.
Если определение УЯЕ ЕХРОЕТ в программе предшествует жую 1 кз с . )зрр, используется модель разделения. // Использование модели разделения йс)ей1пе БЯЕ ЕХРОЕТ $1пс1пйе "юу11гзс.)зрр" Если в программе не определен символ БЯЕ ЕХРОЙТ, используется модель включения, поскольку в этом случае в юуййгвс.)трр автоматически включаются определения из юуТ1гвк.срр. // Использование модели включения $1пс1ис)е "юуТ1квт.
)зрр" Код получился достаточно гибким, однако еще раз подчеркнем, что эти две модели, помимо очевидных логических различий, имеют и тонкие семантические различия. Заметим, что можно также явно инстанцировать экспортируемые шаблоны. В этом случае определение шаблона может находиться в другом файле. Чтобы иметь возможность выбора между моделью включения, моделью разделения и явным ннстанцированием шаблонов, можно сочетать организацию кода, управляемого с помощью БЯЕ ЕХРОЕТ, с соглашениами, описанными в разделе 6.2.2. 6.4. Шаблоны и 1нйпе Чтобы сократить время выполнения программ, небольшие по размерам функции обычно объявляются как встраиваемые. Спецификатор 1п11пе указывает, что в месте вызова функции следует отдавать предпочтение встраиванию тела функции, а не механизму обычного вызова.
Однако выполнять такую встроенную подстановку в месте вызова компилятор не обязан. Как шаблоны функций, так и встраиваемые функции могут быть определены в нескольких единицах трансляции. Обычно это делается путем помещения определения в заголовочный файл, который включается несколькими .С-файлами. На основе сказанного выше может сложиться впечатление, что шаблоны функций являются встраиваемыми по умолчанию, однако это не так. При написании шаблонов б 5. Предварительно откомпилированные заголовочные файлы 95 функций, которые должны обрабатываться как встраиваемые, необходимо явно использовать спецификатор зп1ьпе (если только функция уже не является встраиваемой, поскольку ее определение находится внутри объявления класса). Следовательно, многие небольшие по размерам шаблоны функций, которые не являются частью определения класса„следует объявлять с помощью 1.п1 зпо .
3 6.5. Предварительно откомпилированные заголовочные файлы Заголовочные файлы С++ могут достигать больших размеров даже без использования шаблонов, вследствие чего их компиляция занимает много времени. Применение шаблонов еще более усугубляет ситуацию, поэтому для того, чтобы удовлетворить требования программистов ко времени компиляции, производители программного обеспечения во многих случаях обеспечивают возможность работы с так называемыми предварипзельпо откомпилированными заголовочными файлами. Эта модель не включена в стандарт и зависит от конкретного производителя. Подробное описание создания и использования предварительно откомпилированных заголовочных файлов читатель может найти в документации к различным компиляторам С++, которые поддерживают эту возможность; тем не менее полезно иметь представление о том, как работает это средство.
При компиляции файла компилятор начинает д~начала файла и проходит его до конца. При обработке каждой лексемы файла (которые могут поступать и из файлов, включенных с помощью директивы 61пс1исзе) компилятор изменяет свое внугреннее состояние, например добавляя записи в таблицу символов. По окончании этой работы компилятор может генерировать код в объектных файлах. В основе механизма использования предварительно откомпилированных заголовочных файлов лежит следующее соображение: код можно организовать таким образом, чтобы несколько файлов начинались с одних и тех же строк кода. Теперь предположим, что все файлы, которые требуется откомпилировать, начинаются с одних и тех же )л' строк кода. Тогда можно откомпилировать первые М строк и полностью сохранить состояние компилятора в этот момент в так называемом предварительно откомпилированном заголовочном Файле.
Таким образом, при компиляции каждого файла в программе можно повторно загрузить сохраненное состояние откомпилированного кода и начать его компиляцию со строки И+1. Дело в том, что операция повторной загрузки сохраненного состояния выполняется на несколько порядков быстрее, чем реальная компиляция первых )л строк кода, однако, как правило, требует большего расхода машинных ресурсов, чем простая компиляция. Это увеличение по грубым оценкам составляет от 20 до 200%. Для повышения эффективности использования предварительно откомпилированных заголовочных файлов необходимо добиться того, чтобы подлежащие компиляции файлы з В дальнейшем в книге это правило применяется не всегда, поскольку это может уводить нвс от рассматриваемой темы.
Глава 6. Применение шаблонов на практике 96 по возможности начинались с максимального количества одинаковых строк кода. На практике это означает, что файлы должны начинаться с одних и тех же директив №1пс1иде — именно они потребляют значительную часть времени компиляции. Следовательно, необходимо обратить особое внимание на порядок включения заголовочных файлов. Например, для файлов №1пс1иде <1овггеал> №1пс1исе <чессог> №1пс1ис1е <11вс> №1пс1иде <11вс> №1пс1ийе <зтесгог> не имеет смысла использовать предварительно откомпилированные заголовочные файлы, поскольку исходный код файлов начинается неодинаково.
Некоторые программисты считают, что лучше подключить с помощью №1пс1ийе несколько дополнительных ненужных заголовочных фалов, чем отказаться от возможности ускорения компиляции за счет предварительно скомпилированного заголовочного файла. Такой подход значительно упрощает стратегию включения кода. Например, обычно достаточно просто создать заголовочный файл с именем вгб. Ьрр, включающий все стандартные заголовочные файлы . Такой файл предварительно компилируется, после чего каждую программу, в которой используется стандартная библиотека, можно просто начинать с директивы №1пс1ийе "вой.Ьрр" Обычно на включение такого файла при компиляции требуется немалое время, однако, если в системе достаточно памяти, механизм использования предварительно откомпилированных заголовочных файлов обеспечивает значительно более быструю обработку кода в сравнении с включением каждого отдельного стандартного заголовочного файла без предварительной компиляции.
Особенно хорошо для этого подходят стандартные заголовочные файлы, поскольку они редко изменяются, а следовательно, предварительно 4 Теоретически стандартные заголовочные файлы не обязательно должны соответствовать реальным физическим файлам. На практике, однако, зто так, причем такие файлы очень велики по размерам. №1пс1иде №1пс1ийе №1пс1ийе №1пс1ибе №1пс1ибе <1овсгеалт> <всг1пд> <иессог> <дедие> <11вс> б,5.
Предварительно откомпилированные заголовочные файлы 97 откомпилированный заголовочный файл для нашего файла вес).)трр можно создать один раз . Предварительно откомпилированные заголовочные файлы обычно являются частью конфигурации зависимостей проекта (например, при необходимости их обновление обеспечивается программой ваКе). Один из привлекательных подходов к использованию предварительно скомпилированных заголовочных файлов заключается в создании уровней предварительно скомпилированных заголовочных файлов. Такие уровни начинаются с наиболее широко используемых и стабильных заголовочных файлов (например, это может быть наш н сг1 . Ьрр) и заканчиваются заголовочными файлами, которые предположительно какое-то времв будут оставаться неизменными и поэтому их предварительная компиляция имеет смысл. Когда заголовочные файлы находятся в стадии интенсивной разработки, создание предварительно откомпилированного файла может занимать больше времени, чем составляет экономия за счет его повторного использования.
Ключевой момент этого подхода заключается в том„что предварительно откомпилированный заголовочный файл для более стабильного уровня может быть повторно использован в целях улучшения времени предварительной компиляции менее стабильного заголовочного файла. Например, предположим, что, помимо нашего заголовочного файла нес).)грр (предварительно откомпилированного), создается еще один заголовочный файл — соке.Ьрр, включающий дополнительные возможности, специфические для нашего проекта, но при этом имеющий определенный уровень стабильности.
йфпс1ис)е "вес).)зрр" Мзпс1ис)е "соте бака.)зрр" Фьпс1цс)е "соте а1ссв.)зрр" Поскольку приведенный выше файл начинается с йзпс1ис)е а вес)г)зрр", компилятор может загрузить предварительно откомпилированный заголовочный файл и продолжать работу со следующей строки без повторной компиляции всех стандартных заголовочных файлов. Когда файл будет полностью обработан, создается новый предварительно откомпилированный заголовочный файл. После этого в приложениях можно будет использовать йзпс1ис)е "соке . Нрр", что обеспечит быстрый доступ к большему количеству функциональных возможностей, поскольку компилятор может загружать последний предварительно откомпилированный заголовочный файл.
5 Некоторые члены комитета по стандарту С++ считают концепцию современного заголовочного файла все. Ьрр настолько удобной, что предложили ввести его в стандарт, поэтому не исключено, что мы получим возможность писать йьпс1ибе <все>. Предлагается даже неявное включение этого файла, чтобы все возможности стандартной библиотеки были доступны даже без директив йьпс1цбе. Глава 6.
Применение шаблонов на практике 6.6. Отладка шаблонов При отладке шаблонов возникают два вила проблем. Первый — это проблемы авторов шаблонов: как удостовериться, что создаваемый шаблон будет функционировать для любых аргументов, удовлетворяющих условиям, которые документированы его создателем? Второй — проблемы другой заинтересованной стороны: как может пользователь шаблона выявить, какие из требований к параметрам шаблона нарушены, если шабдон функционирует не так, как указано в документации? Прежде чем подробно рассматривать этот вопрос, целесообразно обсудить, какого рода ограничения могут накладываться на параметры шаблонов. Данный раздел в основном посвящен ограничениям, несоблюдение которых приводит к ошибкам компиляции.