Spec-Zone.ru › GCC 15

8.5 Где находится шаблон?

Шаблоны C++ стали первой языковой возможностью, для поддержки которой от окружения требовалось больше интеллектуальных возможностей, чем традиционно было доступно в системах UNIX. Компилятор и компоновщик должны каким-то образом гарантировать, что каждый экземпляр шаблона, если он нужен, присутствует в исполняемом файле ровно один раз, а если не нужен — не присутствует вовсе. Существует два основных подхода к этой проблеме, называемых моделью Borland и моделью Cfront.

Модель Borland

В Borland C++ проблему инстанцирования шаблонов решили, добавив в компоновщик механизм, эквивалентный общим блокам: компилятор генерирует экземпляры шаблонов в каждой единице трансляции, где они используются, а компоновщик объединяет их. Преимущество этой модели в том, что компоновщику нужно учитывать только сами объектные файлы; нет никакой внешней сложности, о которой пришлось бы беспокоиться. Недостаток в том, что время компиляции увеличивается, поскольку код шаблонов компилируется многократно. Код, написанный для этой модели, обычно содержит определения всех шаблонов в заголовочном файле, поскольку компилятор должен видеть их для инстанцирования.

Модель Cfront

Транслятор C++ компании AT&T, Cfront, решил проблему инстанцирования шаблонов, введя понятие репозитория шаблонов — автоматически поддерживаемого хранилища экземпляров шаблонов. Более современная версия репозитория работает следующим образом: при сборке отдельных объектных файлов компилятор помещает в репозиторий все встреченные определения и экземпляры шаблонов. Во время компоновки обёртка компоновщика добавляет объекты из репозитория и компилирует все необходимые экземпляры, которые ещё не были сгенерированы. Преимущества этой модели — более высокая скорость компиляции и возможность использовать системный компоновщик; для реализации модели Borland поставщику компилятора также пришлось бы заменить компоновщик. Недостатки — значительно возросшая сложность и, как следствие, вероятность ошибок; для некоторых программ эта модель может быть столь же прозрачной, однако на практике бывает очень трудно собирать несколько программ в одном каталоге и одну программу в нескольких каталогах. Код, написанный для этой модели, обычно выносит определения шаблонных методов-членов, не являющихся встроенными, в отдельный файл, который следует компилировать отдельно.

G++ реализует модель Borland для целевых платформ, компоновщики которых её поддерживают, включая платформы ELF (например, GNU/Linux), macOS и 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, при компиляции файлов, в которых не выполняется «#include» определений шаблонных методов-членов, вероятно, можно обойтись без параметра -fno-implicit-templates.

    Если для инстанцирования используется один большой файл, возможно, его стоит скомпилировать без -fno-implicit-templates, чтобы получить все экземпляры, необходимые для явных инстанцирований (но не для других файлов), не перечисляя их отдельно.

    Помимо предварительного объявления явных инстанцирований (с помощью extern), G++ расширяет синтаксис инстанцирования шаблонов, позволяя инстанцировать данные, необходимые компилятору для класса-шаблона (то есть таблицу виртуальных функций), не инстанцируя его методы (с помощью 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-15.3.0/gcc/Template-Instantiation.html

Spec-Zone.ru

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