Spec-Zone.ru › GCC 12

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++ не реализует ни одну автоматическую модель.

У вас есть следующие варианты работы с инстанцированиями шаблонов:

  1. Ничего не делать. Код, написанный для модели 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>&);

    для каждого необходимого экземпляра и создать библиотеку инстанцирования шаблонов из них.

    Это самый простой вариант, но также предлагает гибкость и тонкую настройку при необходимости. Это также самый переносимый вариант, и программы, использующие этот подход, будут работать с большинством современных компиляторов.

  2. Скомпилируйте код с -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-12.2.0/gcc/Template-Instantiation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API