Несколько определений шаблона функции

Предположим, что файл заголовка определяет шаблон функции. Теперь предположим, что два файла реализации #include этот заголовок, и каждый из них имеет вызов шаблона функции. В обоих файлах реализации шаблон функции создается с тем же типом.

// header.hh
template <typename T>
void f(const T& o)
{
    // ...
}

// impl1.cc
#include "header.hh"

void fimpl1()
{
    f(42);
}

// impl2.cc
#include "header.hh"

void fimpl2()
{
    f(24);
}

Можно ожидать, что компоновщик будет жаловаться на несколько определений f(). В частности, если f() не будет шаблоном, это действительно так.

  • Почему линкер не жалуется на несколько определений f()?
  • В стандарте указано, что компоновщик должен обработать эту ситуацию изящно? Другими словами, могу ли я всегда рассчитывать на программы, подобные описанным выше, для компиляции и ссылки?
  • Если компоновщик может быть достаточно умным, чтобы устранить неоднозначность набора экземпляров шаблонов функций, почему он не может сделать то же самое для обычных функций, учитывая, что они идентичны, как в случае с экземплярами шаблонов функций?

Ответ 1

Чтобы поддерживать С++, компоновщик достаточно умен, чтобы признать, что они все одинаковы и выбрасывают все, кроме одного.

РЕДАКТИРОВАНИЕ: разъяснение: Компонент не сравнивает содержимое функции и определяет, что они одинаковы. Шаблонные функции отмечены как таковые, и компоновщик распознает, что они имеют одинаковые подписи.

Ответ 2

В руководстве для компилятора Gnu С++ есть хорошее обсуждение этого вопроса. Выдержка:

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

Модель Borland

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

Модель Cfront

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

При использовании с GNU ld версии 2.8 или позже в системе ELF, такой как GNU/Linux или Solaris 2 или на Microsoft Windows, g++ поддерживает Модель Borland. В других системах g++ не использует ни автоматическую модель.

Ответ 3

Это более или менее частный случай только для шаблонов.

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

Поскольку это трудно решить (стандарт имеет ключевое слово extern для шаблонов, но g++ не реализует его), компоновщик просто принимает несколько определений.