2009-10-28 10 views
31

Non ho idea di cosa stia succedendo nei modelli C++, ma sto cercando di implementare una funzione che cerca un vettore per un elemento che soddisfa una determinata proprietà (in questo caso, la ricerca per uno con il nome dato). La mia dichiarazione nel mio file h è la seguente:Il problema del modello causa l'errore del linker (C++)

template <typename T> 
T* find_name(std::vector<T*> v, std::string name); 

Quando compilo, ottengo questo errore linker quando chiamo la funzione:

Error 1 error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" ([email protected]@@@@[email protected]@[email protected]@@[email protected]@@@[email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" ([email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z) place.obj Program2 

Ancora una volta, Sono nuovo di modelli in modo che non so cosa sta succedendo Tutte le istanze che ho trovato di LNK2019 tramite Google hanno riguardato il non utilizzo delle librerie corrette, ma poiché questa è la mia funzione, non vedo perché ciò accadrebbe.

Inoltre, una domanda correlata: esiste un modo per creare un parametro di modello in modo che debba essere una sottoclasse di una determinata classe, ovvero il modello?

+0

quale compilatore stai utilizzando? alcuni compilatori ti impediscono di separare la dichiarazione e la definizione in file separati per i modelli. – Jordan

+0

Hai davvero scritto un'implementazione per la tua funzione template? – begray

+2

Si può anche prendere in considerazione l'utilizzo di std :: find o std :: find_if –

risposta

59

È necessario avere le definizioni del modello disponibili nel sito di chiamata. Ciò significa che non ci sono file .cpp.

Il motivo è che i modelli non possono essere compilati. Pensa a funzioni come cookie e il compilatore è un forno.

I modelli sono solo un cookie cutter, perché non sanno che tipo di cookie sono. Indica al compilatore solo come eseguire la funzione quando viene fornito un tipo, ma di per sé non può essere utilizzato perché non è in esecuzione alcun tipo di calcestruzzo. Non puoi cucinare un cookie cutter. Solo quando hai preparato la gustosa pasta biscottata (cioè, dato al compilatore l'impasto [tipo]) puoi tagliare il biscotto e cuocerlo.

Allo stesso modo, solo quando si utilizza effettivamente il modello con un determinato tipo, il compilatore può generare la funzione effettiva e compilarla. Tuttavia, non può farlo se manca la definizione del modello. Devi spostarlo nel file di intestazione, in modo che il chiamante della funzione possa creare il cookie.

+0

Grazie. Ora lo capisco, ma temo di non capire del tutto perché funzioni per funzioni regolari, ma non per funzioni di template, che suppongo di poter scrivere su una piena comprensione del C++. – marsolk

+0

I modelli sono piastre, un po 'come i macro, solo meglio (e peggio;). Fino a quando ea meno che non li usi, il compilatore non è obbligato a fare una sostituzione di tipo macro con i tipi specificati (s) e creare la funzione effettiva. Ora, quando il compilatore raggiunge questa istanza, potrebbe o potrebbe non avere il boilerplate in mano. Se non colpisci un posto di blocco. – dirkgently

+1

I modelli di funzioni sono * non * funzioni. I modelli di funzioni sono * modelli *. Obbediscono alle loro stesse regole. – AnT

0

Hai inserito la definizione della funzione del modello in un file cpp? Quindi spostalo nell'intestazione e in linea.

+0

No, questo non è richiesto. Vedi la risposta di Charles Bailey sopra. –

29

Probabilmente stai soffrendo di mancanza di un'istanza valida. Se si inserisce la definizione del modello in un file .cpp separato, quando il compilatore compila il file, potrebbe non sapere quali istanze sono necessarie. Viceversa, nei siti di chiamata che istanziano la versione corretta della funzione template, se la definizione del corpo della funzione non è disponibile il compilatore non avrà le informazioni per creare istanze delle specializzazioni richieste.

Hai due opzioni. Metti il ​​corpo della funzione per il modello di funzione nel file di intestazione.

ad es. nel file di intestazione:

template <typename T> 
inline T* find_name(std::vector<T*> v, std::string name) 
{ 
    // ... 
} 

o esplicitamente istanziare il modello nella cpp in cui avete definito il modello.

ad es. nel file di origine (probabilmente richiederà #include ing il file che definisce Item):

template <typename T> 
T* find_name(std::vector<T*> v, std::string name) 
{ 
    // ... 
} 

template Item* find_name<Item>(std::vector<Item*> v, std::string name); 
+1

Trovo che questo sia più utile della risposta accettata a causa di questo ottimo suggerimento circa la forzatura dell'istanza nel file C++. – ancientHacker

9

Le risposte qui sono fantastici.

io solo aggiungere che questo è spesso il motivo per cui, oltre a .h e .cpp file in un progetto. Troverete spesso i file .inl. Le definizioni del modello andranno nel file .inl.

Questi file .inl significano in linea e di solito inclusi dal file .h con lo stesso prefisso del nome alla fine del file, dopo tutte le dichiarazioni di intestazione. Ciò li rende effettivamente parte del file di intestazione ma separa le dichiarazioni da qualsiasi definizione.

Dal momento che sono glorificati file di intestazione si dovrebbe prendere tutte le stesse precauzioni che si farebbe con un normale file di intestazione, vale a dire sono le protezioni ecc

+0

Questi file '.inl' sono supportati dallo standard C++ o dipendono dal compilatore? – kim366

0

ho appena notato che hai avuto una seconda domanda che sembra essere senza risposta:

c'è un modo per fare un parametro di modello in modo che deve essere una sottoclasse di una certa classe, vale a dire template?

È possibile. Ad esempio, vedere is_base_of in Boost.TypeTraits.

Tuttavia, sono curioso: perché lo vuoi? Normalmente, i requisiti di un modello sui suoi parametri non sono sul tipo stesso del parametro, ma su quali espressioni che coinvolgono quel tipo sono legali. Per esempio, immaginate di avere:

template<class T> 
void foo(const T& t) 
{ 
    if (t.foo()){ 
     t.bar("blah"); 
    } 
} 

Dire che T deve ereditare da qualcosa come:

class HasFooAndBar 
{ 
public: 
    void foo()const; 
    void bar(const char*)const; 
}; 

porta nulla perché l'istanza della funzione non riuscirà in ogni caso se il tipo non supporta le operazioni di . Inoltre, limita inutilmente l'applicabilità di foo(). In realtà, di foo eventuali requisiti sono che t.foo()and t.bar(const char*) sono espressioni valide su un T. const Ad esempio, questo tipo non eredita da HasFooAndBar ed è ancora un foo valido() Parametro:

struct DifferentFromHasFooAndBar 
{ 
    bool foo()const; 
    std::string bar(const std::string&)const; 
}; 
2

Siamo capitati per lo stesso problema e trovato questo che afferma 3 soluzioni alternative: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp

Tra loro è un semplice in cui si crea un metodo "fittizio" nel file .cpp, che chiama la funzione modello/classe con i diversi tipi. Incollato dal link:

// No need to call this TemporaryFunction() function, it's just to avoid link error. 
void TemporaryFunction() 
{ 
    TestTemp<int> TempObj; 
    TestTemp<float> TempObj2; 
} 
+2

Mi piace molto questo metodo, ma c'è un modo per assicurarsi che il compilatore non ottimizzi questa cosa? Una volta ho provato l'ottimizzazione di -O3 e quindi il 'simbolo non definito' – darwinsenior

Problemi correlati