2015-07-31 8 views
5

Ricevo un comportamento coerente sia da gcc 4.8.3 che da clang 3.2, ma non capisco perché sta accadendo. Nonostante io abbia un'istanza esplicativa per un modello di classe, il codice non viene generato e ottengo un simbolo indefinito quando sto usando un'istanza completamente specializzata del modello.Nessun codice generato per modello esplicitamente specializzato anche con istanziazione esplicita

Ho una semplice definizione di modello di classe in un file 'temp.hpp'

#pragma once 

template <typename T1> 
class C 
{ 
public: 
    C (T1 c) : d_c(c) {}; 
    ~C() = default; 

    void print(); 
private: 
    T1 d_c; 
}; 

Nota che il 'print()' metodo è dichiarato, ma non definito qui. Voglio la definizione nel file .cpp e sarà specializzata per diversi tipi.

Così nel file temp.cpp ho la definizione di default del metodo di stampa()

#include "temp.hpp" 
#include <iostream> 

template<typename T1> 
void 
C<T1>::print() 
{ 
    std::cout << "Printing: " << d_c << std::endl; 
} 

seguita da una specializzazione della classe per il tipo 'float':

template <> 
class C <float> 
{ 
public: 
    C (float f) : d_f(f) {}; 
    ~C() = default; 

    void print() 
    { 
    std::cout << "float: " << d_f << std::endl; 
    } 

private: 
    float d_f; 
}; 

e poiché le definizioni sono nel file .cpp, devo esplicitare esplicitamente tutte le specializzazioni che userò. Così ho:

template class C<int>; 
template class C<float>; 

Il driver per il mio test si presenta così in test.cpp:

#include "temp.hpp" 

int main() 
{ 
    int i = 1; 
    C<int> c_int(i); 

    float f = 1.2; 
    C<float> c_float(f); 

    c_int.print(); 
    c_float.print(); 
} 

Su compilazione e collegamento questo ottengo errore:

test.cpp: undefined reference to `C<float>::print()' 

Il codice oggetto per C < int> è generato correttamente. Posso vedere usando nm:

nm -C temp.o 
... 
0000000000000000 W C<int>::print() 
0000000000000000 W C<int>::C(int) 
0000000000000000 W C<int>::C(int) 
... 

Come ho già detto in precedenza, questo è coerente con gcc e clang così sto assumendo c'è qualche regola di lingua che non capisco qui.

Si noti che se aggiungo un utilizzo del metodo print() nel file temp.cpp, il codice viene generato, ma quello è sciocco e nel mio codice reale sarebbe impossibile. Per questo semplice caso di test sarebbe simile:

void foo() 
{ 
    C<float> s(1.3); 
    s.print(); 
} 

Nel codice vero e proprio, che ha motivato questo piccolo test il mio modello dispone di 3 argomenti di template che si combinano per espandersi in circa 30 permutazioni del codice. Ce ne sono uno o due per i quali ho bisogno di una specializzazione che faccia qualcosa di diverso, ma l'altro 28 posso lasciare da solo.

Qualsiasi suggerimento su dove ho sbagliato o un riferimento al linguaggio per il motivo per cui l'istanziazione esplicita di non dovrebbe generare codice è molto apprezzato. Ho passato 1/2 giorno a leggere tutti gli altri post StackOverflow su istanziazione esplicita e credo di usarlo correttamente.

+0

modo da avere la specializzazione per '' C in 'temp.cpp', ma si desidera utilizzare che la specializzazione in' test.cpp'? Questo é un problema. Il tipo specializzato 'C ' è un tipo diverso e completamente indipendente da quello che si otterrebbe se si sostituisse 'float' per' T1' nel modello originale. In 'test.cpp', sta cercando' C :: print() 'dove' T1 == float', e questo non esiste nel tuo programma. Se sposti la specializzazione nell'header 'temp.hpp', allora dovrebbe funzionare; la specializzazione deve essere visibile al compilatore durante l'elaborazione di 'test.cpp'. –

+0

Grazie per la spiegazione Jason, Nel codice reale la quantità di codice in specializzazione è sufficientemente complessa da disegnare in un mucchio di altri file di intestazione e finirei nell'intestazione Hades per scopi di compilazione. Ho trovato una soluzione alternativa in cui posso mantenere le espansioni che generano codice illecito più in alto nella gerarchia di espansione. – Jay

risposta

6

Da [temp.expl.spec]:

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.

stiamo specializzata esplicitamente C in temp.cpp, ma in test.cpp, non è dichiarata prima di essere utilizzato. Pertanto, il tuo codice è mal formato, non è richiesta alcuna diagnostica. Dovrai semplicemente spostare la dichiarazione di C<float> in temp.hpp

Fare sempre attenzione con le specializzazioni esplicite. Lo standard li porta molto sul serio:

The placement of explicit specialization declarations for function templates, class templates, [...], can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

+2

Non hai citato il paragrafo seguente. Sono deluso :) –

+0

@ T.C. Manca una fantastica opportunità. – Barry

+0

Grazie per le risposte, odierò che il mio programma si auto-immolasse, il mio capo diventerebbe piuttosto matto;) Immagino che la mia domanda di follow-up sia riuscita a causa di una specializzazione parziale invece di una specializzazione esplicita? Questo mi è sfuggito in modo sottile prima. – Jay

Problemi correlati