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-10.5.0/gcc/Template-Instantiation.html