7.5 Где шаблон?
Шаблоны C++ — это первая языковая функция, потребовавшая от среды большей интеллектуальности, чем традиционно ожидалось в UNIX-системах. Компилятору и линковщику каким-то образом нужно убедиться, что каждая экземпляризация шаблона появляется ровно один раз в исполняемом файле, если она требуется, и не появляется вообще в противном случае. Существует два основных подхода к этой проблеме, которые называются моделью Borland и моделью Cfront.
- Модель Borland
-
Borland C++ решил проблему экземпляризации шаблонов, добавив в свой линковщик код, эквивалентный общим блокам; компилятор генерирует экземпляризации шаблонов в каждом модуле трансляции, использующем их, а линковщик сворачивает их вместе. Преимущество этой модели заключается в том, что линковщику нужно рассматривать только сами объектные файлы; внешней сложности нет. Недостаток заключается в увеличении времени компиляции, поскольку код шаблона компилируется повторно. Код, написанный для этой модели, как правило, включает определения всех шаблонов в заголовочный файл, поскольку они должны быть видны для экземпляризации.
- Модель Cfront
Переводчик AT&T C++, Cfront, решил проблему экземпляризации шаблонов, создав понятие хранилища шаблонов — автоматически поддерживаемого места, где хранятся экземпляризации шаблонов. Более современная версия хранилища работает следующим образом: при построении отдельных объектных файлов компилятор помещает любые определения и экземпляризации шаблонов, встреченные в хранилище. На этапе линковки оболочка линковщика добавляет объекты из хранилища и компилирует любые необходимые экземпляризации, которые ранее не были сгенерированы. Преимущества этой модели — более оптимальная скорость компиляции и возможность использования системного линковщика; для реализации модели Borland поставщик компилятора также должен заменить линковщик. Недостатки — значительно увеличенная сложность и, следовательно, потенциальные ошибки; для некоторых кодов это может быть так же прозрачно, но на практике это может быть очень сложно при построении нескольких программ в одном каталоге и одной программы в нескольких каталогах. Код, написанный для этой модели, как правило, разделяет определения нелинейных шаблонов членов в отдельный файл, который должен компилироваться отдельно.
G++ реализует модель Borland на целевых платформах, где линковщик ее поддерживает, включая ELF-целевые платформы (например, GNU/Linux), Mac OS X и Microsoft Windows. В противном случае G++ не реализует ни одну из автоматических моделей.
У вас есть следующие варианты для работы с экземпляризациями шаблонов:
- Ничего не делать. Код, написанный для модели Borland, работает нормально, но каждый модуль трансляции содержит экземпляризации каждого используемого шаблона. Дублированные экземпляризации будут удалены линковщиком, но в большой программе это может привести к неприемлемому количеству дублирования кода в объектных файлах или динамических библиотеках.
Дублирование экземпляризаций шаблона можно избежать, определив явную экземпляризацию в одном объектном файле и предотвратив неявную экземпляризацию компилятором в других объектных файлах, используя объявление явной экземпляризации, используя синтаксис
extern template:extern template int max (int, int);
Этот синтаксис определен в стандарте C++ 2011, но поддерживается G++ и другими компиляторами с давних пор.
Явные экземпляризации можно использовать для самых больших или наиболее часто дублируемых экземпляризаций, не зная точно, какие другие экземпляризации используются в остальной части программы. Вы можете разбросать явные экземпляризации по всей программе, возможно, поместив их в модули трансляции, где используются экземпляризации, или в модули трансляции, которые определяют сами шаблоны; вы можете поместить все необходимые явные экземпляризации в один большой файл; или вы можете создать небольшие файлы, как
#include "Foo.h" #include "Foo.cc" template class Foo<int>; template ostream& operator << (ostream&, const Foo<int>&);для каждого необходимого экземпляра и создать библиотеку экземпляризаций шаблонов из них.
Это самый простой вариант, но также предлагает гибкость и тонкую настройку при необходимости. Это также самый переносимый вариант, и программы, использующие этот подход, будут работать с большинством современных компиляторов.
- Скомпилируйте код, использующий шаблоны, с помощью -frepo. Компилятор генерирует файлы с расширением ‘.rpo’, перечисляющие все экземпляризации шаблонов, используемые в соответствующих объектных файлах, которые могут быть экземпляризованы там; оболочка линковщика, ‘collect2’, затем обновляет файлы ‘.rpo’, чтобы сообщить компилятору, где разместить эти экземпляризации, и пересобрать любые затронутые объектные файлы. Накладные расходы на время линковки незначительны после первого прохода, так как компилятор продолжает размещать экземпляризации в тех же файлах.
Это может быть подходящим вариантом для прикладного кода, написанного для модели Borland, поскольку, как правило, это просто работает. Код, написанный для модели Cfront, необходимо изменить, чтобы определения шаблонов были доступны в одной или нескольких точках экземпляризации; обычно это делается просто добавлением
#include <tmethods.cc>в конец каждого заголовочного файла шаблона.Для кода библиотеки, если вы хотите, чтобы библиотека предоставляла все необходимые экземпляризации шаблонов, просто попробуйте связать все свои объектные файлы вместе; ссылка завершится неудачей, но в качестве побочного эффекта приведет к генерации экземпляризаций. Однако будьте осторожны, так как это может вызвать конфликты, если несколько библиотек пытаются предоставить одни и те же экземпляризации. Для лучшего контроля используйте явную экземпляризацию, как описано в следующем пункте.
- Скомпилируйте код с помощью -fno-implicit-templates для отключения неявной генерации экземпляризаций шаблонов и явной экземпляризации всех используемых экземпляризаций. Этот подход требует больше знаний о том, какие именно экземпляризации вам нужны, чем другие, но он менее загадочен и позволяет больше контроля, если вы хотите гарантировать, что используются только предназначенные экземпляризации.
Если вы используете код модели Cfront, вы, вероятно, сможете обойтись без -fno-implicit-templates при компиляции файлов, не включающих (‘#include’) определения шаблонов членов.
Если вы используете один большой файл для экземпляризаций, вы можете скомпилировать его без -fno-implicit-templates, чтобы получить все экземпляризации, необходимые вашим явным экземпляризациям (но не другими файлами), не указывая их также.
В дополнение к предварительной декларации явных экземпляризаций (с
extern), G++ расширил синтаксис экземпляризации шаблонов для поддержки экземпляризации данных поддержки компилятора для класса шаблона (т. е. vtable) без экземпляризации каких-либо его членов (сinline) и экземпляризации только статических данных членов класса шаблона без данных поддержки или функций членов (сstatic) :inline template class Foo<int>; static template class Foo<int>;
Далее: Связанные функции-члены, Предыдущее: Интерфейс C++, Вверх: Расширения C++ [Содержание][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gcc/Template-Instantiation.html