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