Как экземпляры шаблона ссылок С++

Если я определяю функцию (возможно, функцию члена класса, но не встроенную) в файл заголовка, который включается двумя разными единицами перевода, я получаю ошибку связи, поскольку эта функция многократно определена. Не так с шаблонами, поскольку они не являются компилируемыми типами, пока компилятор не разрешит объявление объекта шаблонизированного типа. Это заставило меня понять, что я не знаю, где находится скомпилированный код шаблона и как он связан, поскольку С++ не просто создает несколько копий кода для определения SomeTemplateClass. Любая дополнительная информация будет очень полезна. Спасибо!

Ответ 1

Существуют 3 схемы реализации, используемые компиляторами С++:

  • жадный экземпляр, где компилятор генерирует экземпляр в каждом модуле компиляции, который его использует, тогда компоновщик выбрасывает всех, кроме одного из них (это не просто оптимизация размера кода, требуется, чтобы адреса функций, static и т.п.). Это наиболее распространенная модель.

  • запросил экземпляр, где у компилятора есть база данных уже выполненных им экземпляров. Когда требуется создание экземпляра, БД проверяется и обновляется. Единственный компилятор, который я знаю, который использует это, - это Sun, и он больше не используется по умолчанию.

  • повторяется экземпляр, где экземпляры выполняются компоновщиком (либо напрямую, либо путем назначения их компиляционной единице, которая затем будет перекомпилирована). Это модель, используемая CFront, т.е. Исторически она была первой, а также компиляторами, использующими front-end EDG (с некоторыми оптимизациями по сравнению с CFront).

(См. С++ Templates, The Complete Guide от David Vandevoorde и Nicolai Josuttis. Еще одна онлайн-ссылка http://www.bourguet.org/v2/cpplang/export.pdf, которая больше беспокоит модель компиляции, но все еще содержит описания механизмов создания экземпляров).

Ответ 2

Это конкретная реализация.

Некоторые компиляторы будут сгенерировать те же экземпляры шаблонов снова и снова для каждой единицы перевода, в которой они были созданы, и пусть компоновщик сбрасывает дубликаты.
Шаблоны получили плохую репутацию "раздувания кода", когда линкеры еще не достигли этой задачи. В настоящее время это, вероятно, незаслуженно. Некоторые реализации даже сбрасывают разные экземпляры при компиляции одного и того же целевого машинного кода. (Как f<A*>() и f<B*>(), поскольку типы указателей являются только адресами в сгенерированном машинный код.)

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

Все они имеют разные преимущества и недостатки. Отсутствие истинной концепции модуля. Я сомневаюсь, что кто-нибудь придумает идеальную схему.

С export раньше требовалось, чтобы компиляторы предварительно компилировали код шаблона и создавали экземпляр по запросу. Однако, за исключением одного поставщика, никто не реализовал export для своего компилятора, и теперь он удален.

Ответ 3

Все функции шаблона неявно встроены. Так же, как методы, определенные в объявлении класса, неявно встроены.

Когда я говорю неявно, я имею в виду более современное использование этого слова. Посмотрите мое подробное описание здесь.

Короче говоря, inline, static и extern являются всеми директивами по связям с сестрами. inline сообщает компоновщику игнорировать повторяющиеся определения функции. Как правило, это означает, что компоновщик выбирает одно определение и использует его для всех единиц компиляции. Я не знаю каких-либо компиляторов, которые делают или оставляют весь дублирующий код шаблона в конечном исполняемом файле.

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

Ответ 4

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