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>&);для каждого из необходимых экземпляров и создать библиотеку экземпляризации шаблонов из этих файлов.
Это самый простой вариант, но также предлагает гибкость и тонкую настройку, когда это необходимо. Это также самый переносимый вариант, и программы, использующие этот подход, будут работать с большинством современных компиляторов.
- Скомпилируйте код с -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>;
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-13.3.0/gcc/Template-Instantiation.html