2011-07-15 13 views
7

Ho un problema con la specializzazione dei modelli che vorrei capire. Sto lavorando con Visual C++ 10.0 (2010). Ho una classe come questa:La specializzazione del modello non riesce al collegamento

class VariableManager 
{ 
public: 

    template<typename VarT> 
     VarT get(std::string const& name) const 
     { 
        // Some code... 
     } 

    // This method supposed to be fully evaluated, linkable method. 
    template<> 
     std::string get<std::string>(std::string const& name) const; 

private: 
    std::map<std::string, boost::any> mVariables; 
}; 

In teoria, perché mi sono specializzato il metodo "get", il linker dovrebbe essere in grado di raccogliere da un file oggetto. Invece, ottengo un errore riferimento non risolto con il linker se ho posto il metodo nel file di origine:

template<> 
    std::string VariableManager::get<std::string>(std::string const& name) const 
      { 
       // Doing something... 
      } 

Se metto questo metodo nel file di intestazione come linea, la build va più che bene. Capisco che funzioni modello come questo:

 template<typename VarT> 
      VarT get(std::string const& name) const; 

deve essere posto nell'intestazione perché il compilatore non sarà in grado di specializzarsi il modello in base al codice chiamante, ma nel caso della piena specializzazione è il l'implementazione di classe che lo fa, quindi il metodo template specializzato dovrebbe già esistere come simbolo pubblico. Qualcuno potrebbe far luce su questo problema?

risposta

10

La tua analisi è corretta - un modello di funzione esplicitamente specializzato che dispone di tutti i parametri di modello specificate con valori espliciti fornisce una definizione completa di una funzione.

Se è stato incluso correttamente il file .cpp corrispondente che contiene la definizione della specializzazione esplicita nel progetto, VC++ non deve generare un errore del linker. Per quanto riguarda la conformità agli standard, tuttavia, tieni presente che devi dichiarare la tua specializzazione al di fuori dello della classe che li ospita. Lo Standard proibisce di dichiarare le specializzazioni esplicite all'interno della classe che le include (e altri compilatori rifiuteranno il tuo codice). Quindi modificare il file di intestazione per dichiarare la specializzazione in questo modo, invece

class VariableManager 
{ 
public: 

    template<typename VarT> 
     VarT get(std::string const& name) const 
     { 
        // Some code... 
     } 

private: 
    std::map<std::string, boost::any> mVariables; 
}; 

// This method supposed to be fully evaluated, linkable method. 
template<> 
std::string VariableManager::get<std::string>(std::string const& name) const; 

Lasciatemi nota anche che non si può chiamare get<std::string> all'interno del vostro corpo di classe. Questo perché qualsiasi chiamata di questo tipo non vedrebbe ancora la dichiarazione di specializzazione esplicita e quindi proverebbe a creare un'istanza della funzione dalla definizione del modello. Lo standard rende malformato tale codice, senza che sia necessaria una diagnostica.

+0

Divertente, ma sembra che il problema di conformità standard sia stato il problema. Ho rimosso la dichiarazione dalla classe e ho fatto la dichiarazione di specializzazione come da lei suggerito, e il linker non ha avuto problemi. È bene ricordare che le specializzazioni devono essere dichiarate al di fuori dell'ambito della classe. Grazie per il tuo gentile aiuto! – progician

+0

È una vera fortuna per me trovare la tua risposta. È ancora un mistero per me ciò che fa il compilatore se lascio la specializzazione del template all'interno di una classe e cosa rende il linker non in grado di trovarlo. Tuttavia, hai sicuramente salvato la mia giornata! – Nipheris

0

La specializzazione di un modello non costringe il compilatore a istanziarlo (penso che GCC lo faccia comunque); devi ancora dire esplicitamente al compilatore di istanziare realmente il modello. Puoi farlo con l'istanziazione del template esplicita. In pratica, basta aggiungere questo nel file di origine:

template std::string VariableManager::get(const std::string& name) const; 
+0

questo è sbagliato. lo ha specializzato quindi non ha bisogno né vuole istanziare il modello –

+1

litb ha ragione, questo è davvero sbagliato, ma lascerò qui la mia risposta in modo che altri possano vedere che è sbagliato anche –

0

Metodo partendo template<> è ancora considerato un templatespecializzazione metodo. Quindi devi essere messo in un file di intestazione.

Se si desidera inserirlo in un file di implementazione, è necessario sovraccaricare .

class VariableManager 
{ 
//... 
    VarT get(std::string const& name) const 
    {} 

    std::string get(std::string const& name) const; //overloading not specialization 
}; 
+0

No, non lo fai. Vedi la risposta di reko. E non sono sicuro di cosa intendi per "metodo di specializzazione modello". –

+0

(** MA MAI raccomanderei di sovraccaricare la specializzazione _qualunque giorno_!) –

+0

@Tomalak, l'OP sta cercando di specializzare il metodo 'template'' get' nella domanda.La mia risposta è che i metodi specializzati dovrebbero essere dichiarati nei file di intestazione. Puoi specificare di più, cosa c'è che non va? – iammilind

Problemi correlati