Spec-Zone.ru › GCC 8

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

Spec-Zone.ru

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