Spec-Zone.ru › GCC 6

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. Компилируйте свой код, использующий шаблоны, с помощью -frepo. Компилятор создаёт файлы с расширением ‘.rpo’, в которых перечислены все инстанции шаблонов, используемые в соответствующих объектных файлах, которые могли бы быть инстанцированы там; затем обёртка компоновщика ‘collect2’ обновляет файлы ‘.rpo’, чтобы сообщить компилятору, где разместить эти инстанции, и перекомпилировать любые затронутые объектные файлы. Надстройка времени компоновки пренебрежимо мала после первого прохода, поскольку компилятор продолжает размещать инстанции в тех же файлах.

    Это может быть подходящим вариантом для кода приложения, написанного для модели Borland, поскольку он, как правило, просто работает. Код, написанный для модели Cfront, необходимо изменить, чтобы определения шаблонов были доступны в одном или нескольких точках инстанцирования; обычно это делается просто добавлением #include <tmethods.cc> в конец каждого файла заголовков шаблона.

    Для кода библиотеки, если вы хотите, чтобы библиотека предоставляла все необходимые ей инстанции шаблонов, просто попробуйте связать все её объектные файлы вместе; ссылка не удастся, но вызовет инстанциацию как побочный эффект. Однако будьте осторожны, так как это может привести к конфликтам, если несколько библиотек пытаются предоставить одни и те же инстанциации. Для лучшего контроля используйте явную инстанциацию, как описано в следующем варианте.

  3. Компилируйте свой код с помощью -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-6.5.0/gcc/Template-Instantiation.html

Spec-Zone.ru

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