Множественные определения шаблона функции

Предположим, что файл заголовка определяет шаблон функции. Теперь предположим, что это два файла реализации #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()?
  • Указывается ли в стандарте, что компоновщик должен корректно обрабатывать эту ситуацию? Другими словами, всегда ли я могу рассчитывать на программы, подобные приведенным выше, для компиляции и компоновки?
  • Если компоновщик может быть достаточно умен, чтобы устранить неоднозначность набора экземпляров шаблонов функций, почему он не может делать то же самое для обычных функций, учитывая, что они идентичны, как в случае с экземплярами шаблонов функций?

Заголовок вводит в заблуждение ... а как насчет "множественных экземпляров"? Или «экземпляры в отдельных единицах перевода»?

Adam Mitz 25.10.2008 07:16

Я уточнил вопрос. Это довольно широкий вопрос, поэтому я разделил его на более мелкие вопросы. Возможно, мне следует разделить их на несколько ТАКИХ вопросов?

wilhelmtell 25.10.2008 07:55

Я не вижу "множественных определений" f () в вашем коде.

Adam Mitz 25.10.2008 18:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
10
3
6 174
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Для поддержки C++ компоновщик достаточно умен, чтобы распознать, что все они являются одной и той же функцией, и выбрасывает все, кроме одной.

Обновлено: пояснение: Компоновщик не сравнивает содержимое функций и не определяет, что они одинаковы. Шаблонные функции помечаются как таковые, и компоновщик распознает, что у них одинаковые подписи.

Но недостаточно умен, чтобы распознать это, когда f () не является шаблоном?

wilhelmtell 25.10.2008 03:52

Не разрешено, кроме шаблонов.

Head Geek 25.10.2008 03:54

Он делает это и для встроенных функций - если он решает не встраивать их.

Mark Ransom 25.10.2008 06:08

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

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

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

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

C++ templates are the first language feature to require more intelligence from the environment than one usually finds on a UNIX system. Somehow the compiler and linker have to make sure that each template instance occurs exactly once in the executable if it is needed, and not at all otherwise. There are two basic approaches to this problem, which are referred to as the Borland model and the Cfront model.

Borland model

Borland C++ solved the template instantiation problem by adding the code equivalent of common blocks to their linker; the compiler emits template instances in each translation unit that uses them, and the linker collapses them together. The advantage of this model is that the linker only has to consider the object files themselves; there is no external complexity to worry about. This disadvantage is that compilation time is increased because the template code is being compiled repeatedly. Code written for this model tends to include definitions of all templates in the header file, since they must be seen to be instantiated.

Cfront model

The AT&T C++ translator, Cfront, solved the template instantiation problem by creating the notion of a template repository, an automatically maintained place where template instances are stored. A more modern version of the repository works as follows: As individual object files are built, the compiler places any template definitions and instantiations encountered in the repository. At link time, the link wrapper adds in the objects in the repository and compiles any needed instances that were not previously emitted. The advantages of this model are more optimal compilation speed and the ability to use the system linker; to implement the Borland model a compiler vendor also needs to replace the linker. The disadvantages are vastly increased complexity, and thus potential for error; for some code this can be just as transparent, but in practice it can be very difficult to build multiple programs in one directory and one program in multiple directories. Code written for this model tends to separate definitions of non-inline member templates into a separate file, which should be compiled separately.

When used with GNU ld version 2.8 or later on an ELF system such as GNU/Linux or Solaris 2, or on Microsoft Windows, G++ supports the Borland model. On other systems, G++ implements neither automatic model.

Другие вопросы по теме