Definizioni multiple di un modello di funzione
Domanda
Supponiamo che un file di intestazione definisca un modello di funzione. Supponiamo ora due file di implementazione #include
questa intestazione e ognuno di essi ha una chiamata al modello di funzione. In entrambi i file di implementazione il modello di funzione è istanziato con lo stesso tipo.
// 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);
}
Ci si può aspettare che il linker si lamenti di più definizioni di f ()
. In particolare, se f ()
non sarebbe un modello, sarebbe effettivamente così.
- Come mai il linker non si lamenta di più definizioni di
f ()
? - È specificato nello standard che il linker deve gestire questa situazione con garbo? In altre parole, posso sempre contare su programmi simili ai precedenti per compilare e collegare?
- Se il linker può essere abbastanza intelligente da chiarire una serie di istanze di template di funzione, perché non può fare lo stesso per le funzioni regolari, dato che sono identiche come nel caso dei template di funzione istanziati?
Soluzione
Al fine di supportare C ++, il linker è abbastanza intelligente da riconoscere che hanno tutti la stessa funzione e butta fuori tutti tranne uno.
EDIT: chiarimento: Il linker non confronta i contenuti delle funzioni e determina che sono uguali. Le funzioni basate su modelli sono contrassegnate come tali e il linker riconosce di avere le stesse firme.
Altri suggerimenti
Il manuale del compilatore Gnu C ++ contiene una buona discussione di questo . Un estratto:
I modelli C ++ sono la prima lingua caratteristica per richiedere più intelligenza dall'ambiente di solito trova su un sistema UNIX. In qualche modo il compilatore e linker devono essere sicuri che si verifica ogni istanza del modello esattamente una volta nell'eseguibile, se lo è è necessario e per niente altrimenti. Ci sono due approcci di base a questo problema, che sono indicati come Modello Borland e modello Cfront.
Modello Borland
Borland C ++ ha risolto il modello problema di istanza aggiungendo il codice equivalente di blocchi comuni a il loro linker; il compilatore emette istanze del modello in ogni traduzione unità che li utilizza e il linker li fa collassare insieme. Il vantaggio di questo modello è solo il linker deve considerare i file oggetto loro stessi; non c'è esterno complessità di cui preoccuparsi. Questo lo svantaggio è che il tempo di compilazione viene aumentato perché il codice modello viene compilato ripetutamente. Codice scritto per questo modello tende a includere definizioni di tutti i modelli nel file di intestazione, poiché devono essere visto per essere istanziato.
Modello Cfront
Il traduttore AT & amp; T C ++, Cfront, risolto l'istanza del modello problema creando la nozione di a repository di modelli, un automaticamente luogo mantenuto dove modello le istanze sono memorizzate. Un più moderno la versione del repository funziona come segue: Come singoli file oggetto vengono creati, il compilatore ne posiziona uno definizioni dei modelli e istanze incontrate nel repository. Al momento del collegamento, il collegamento wrapper aggiunge gli oggetti in repository e compila ogni necessario istanze che non erano in precedenza emessa. I vantaggi di questo modello sono la velocità di compilazione più ottimale e la capacità di utilizzare il linker di sistema; per implementare il modello Borland a anche il fornitore del compilatore deve sostituire il linker. Gli svantaggi sono complessità notevolmente aumentata, e quindi potenziale errore; per un po 'di codice questo può essere altrettanto trasparente, ma in pratica può essere molto difficile per creare più programmi in uno directory e un programma in più le directory. Codice scritto per questo il modello tende a separare le definizioni di modelli di membri non inline in a file separato, che dovrebbe essere compilato separatamente.
Se usato con GNU ld versione 2.8 o in seguito un sistema ELF come GNU / Linux o Solaris 2 o su Microsoft Windows, G ++ supporta il Modello Borland. Su altri sistemi, G ++ non implementa né il modello automatico.
Questo è più o meno un caso speciale solo per i modelli.
Il compilatore genera solo le istanze del modello effettivamente utilizzate. Poiché non ha alcun controllo su quale codice verrà generato da altri file di origine, deve generare il codice modello una volta per ogni file, per assicurarsi che il metodo venga generato affatto.
Poiché è difficile risolverlo (lo standard ha una parola chiave extern
per i modelli, ma g ++ non lo implementa) il linker accetta semplicemente le definizioni multiple.