Если я определяю функцию (возможно, функцию члена класса, но не встроенную) в файл заголовка, который включается двумя разными единицами перевода, я получаю ошибку связи, поскольку эта функция многократно определена. Не так с шаблонами, поскольку они не являются компилируемыми типами, пока компилятор не разрешит объявление объекта шаблонизированного типа. Это заставило меня понять, что я не знаю, где находится скомпилированный код шаблона и как он связан, поскольку С++ не просто создает несколько копий кода для определения 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
На самом деле он создает несколько копий. Эти копии являются особыми и не нарушают правило с одним определением. Некоторые линкеры придут, удаляют копии и возвращают функции, используя их; не все делают.