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++ и другими компиляторами с момента, значительно предшествующего 2011 году.
Явные экземпляризации можно использовать для самых больших или наиболее часто дублируемых экземпляров, не зная точно, какие другие экземпляры используются в остальной части программы. Вы можете разбросать явные экземпляризации по всей программе, возможно, помещая их в модули трансляции, где экземпляры используются или модули трансляции, которые определяют сами шаблоны; вы можете поместить все необходимые явные экземпляризации в один большой файл; или вы можете создать небольшие файлы, как
#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-9.5.0/gcc/Template-Instantiation.html