2011-08-18 12 views
10

Se una definizione di classe template contiene una variabile membro statica che dipende dal tipo di modello, non sono sicuro di quale dovrebbe essere il comportamento affidabile?Cosa dovrebbe accadere alle variabili membro statiche della classe template con definizione nel file .h

Nel mio caso è auspicabile inserire la definizione di tale membro statico nello stesso file .h come la definizione della classe, dal momento che

  1. voglio la classe di essere generale per molti tipi di dati modello che ho non attualmente sapere.
  2. Voglio solo un'istanza dell'elemento statico da condividere in tutto il mio programma per ogni tipo di modello specificato. (Uno per tutti MyClass<int> e uno per tutti MyClass<double>, ecc

posso essere più breve dicendo che il codice elencato at this link si comporta esattamente come voglio quando viene compilato con GCC 4.3. E 'questo comportamento in base a standard C++ in modo che posso contare su di esso quando si utilizzano altri compilatori?

tale nesso non è il mio codice, ma un esempio di contatore pubblicato da CodeMedic alla discussione here. ho trovato molti altri dibattiti come questo one ma nulla che io considero conclusivo

Penso che Il linker sta consolidando le molteplici definizioni trovate (nell'esempio a.o e b.o). È questo il comportamento del linker richiesto/affidabile?

risposta

18

Da N3290, 14,6:

A [...] membro di dati static di un modello di classe è definita in ogni unità di traduzione in cui è implicitamente un'istanza [...], a meno che il la specializzazione corrispondente è esplicitamente istanziata [...].

In genere, si mette la definizione membro statico nel file di intestazione, insieme con la definizione della classe del modello:

template <typename T> 
class Foo 
{ 
    static int n;      // declaration 
}; 

template <typename T> int Foo<T>::n; // definition 

Per espandere sulla concessione: se si pensa di utilizzare istanze espliciti nel codice, come:

template <> int Foo<int>::n = 12; 

allora è necessario nonmettere la definizione su modelli nell'intestazione se Foo<int> è usato anche in altri tU diversa da quella o ne contenente l'istanziazione esplicita, dal momento che si otterrebbero più definizioni.

Tuttavia, se è necessario impostare un valore iniziale per tutti i parametri possibili senza utilizzare l'istanziazione esplicita, è necessario inserirlo nell'intestazione, ad es.con TMP:

// in the header 
template <typename T> int Foo<T>::n = GetInitialValue<T>::value; // definition + initialization 
+0

Ok, quindi sia il codice di esempio che il comportamento che sto vedendo sono conformi allo standard? In realtà ... ecco cosa manca per suggellare la vittoria: cosa dice che non ci saranno più istanze dello specifico membro statico nel programma, quindi la variabile statica avrebbe lo stesso valore tra tutte le unità di traduzione? Questo per quanto posso capire avrebbe bisogno di essere un po 'di consolidamento al momento del collegamento. – NoahR

+0

@NoahR: tutte le istanze del membro statico saranno collegate alla stessa variabile effettiva, quindi il valore sarà realmente condiviso. Penso che se non si scrive un'inizializzazione esplicita, si ottiene l'inizializzazione di default (o inizializzazione del valore, ho dimenticato, l'impostazione predefinita è zero), ma si potrebbe anche scrivere '= 0;' nel file di intestazione. –

+1

Grazie a @Kerrek SB. Pertanto, i membri statici di una classe template ricevono un trattamento diverso rispetto ai membri statici di una classe non template. Dico questo perché ho trovato un sacco di esempi per concludere che fornire la definizione del membro statico nel file .h per la classe non di modello genera un errore di simbolo duplicato in fase di collegamento ed è una violazione della regola di una definizione. In ogni caso, la domanda in questione: è il consolidamento del collegamento di un membro statico di classe modello richiesto dallo standard? – NoahR

1

Il linker gestisce questi casi quasi esattamente la stessa che per i membri statici della classe non-template con __declspec(selectany) dichiarazione applicata, in questo modo:

class X { 
public: 
X(int i){}; 
}; 
__declspec(selectany) X x(1);//works in msvc, for gcc use __attribute__((weak)) 

E come msdn says: "in fase di collegamento , se vengono visualizzate più definizioni di un COMDAT, il linker ne sceglie uno e scarta il resto ... Per gli oggetti globali inizializzati dinamicamente, selectany eliminerà anche il codice di inizializzazione di un oggetto non referenziato. "

2

Questa è un'aggiunta completa all'eccellente risposta di @Kerrek SB. Lo aggiungerei come commento, ma ce ne sono già molti, quindi i nuovi commenti sono nascosti di default.

Quindi, il suo e altri esempi che ho visto sono "facili" nel senso che il tipo di variabile membro statico è noto in anticipo. È semplice perché il compilatore, ad esempio, conosce le dimensioni di archiviazione per qualsiasi istanza di modello, quindi si potrebbe pensare che il compilatore possa utilizzare lo schema di mangling funky, generare una volta la definizione di variabile e scaricare il resto nel linker e che potrebbe persino funzionare.

Ma è un po 'sorprendente che funzioni quando il tipo di membro statico dipende dal parametro del modello. Ad esempio, in seguito a lavori:

template <typename width = uint32_t> 
class Ticks : public ITimer< width, Ticks<width> > 
{ 
protected: 
    volatile static width ticks; 
} 
template <typename width> volatile width Ticks<width>::ticks; 

(Si noti che istanziazione esplicita di var statica non ha bisogno (o permette) spec di default per "larghezza").

Quindi, porta più pensieri, che il compilatore C++ deve fare un bel po 'di elaborazione - in particolare, per creare un'istanza di un modello, non è necessario solo un modello stesso, ma deve anche raccogliere tutto [membro statico] esplicito le istanziazioni (ci si potrebbe chiedere allora perché sono stati creati costrutti sintattici separati, non qualcosa da enunciare all'interno della classe template).

Per quanto riguarda l'implementazione di questo a livello di linker, per GNU binutils i suoi "simboli comuni": http://sourceware.org/binutils/docs/as/Comm.html#Comm. (Per i toolchain di Microsoft, si chiama COMDAT, come dice un'altra risposta).

Problemi correlati