2009-06-15 21 views
24

In C++, se si definisce questa funzione in header.hppvariabile statica all'interno funzione template

void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

e si include header.hpp in almeno due file cpp. Quindi avrai multiple definition of incAndShow(). Quale è previsto. Tuttavia, se si aggiunge un modello per la funzione

template <class T> 
void incAndShow() 
{ 
    static int myStaticVar = 0; 
    std::cout << ++myStaticVar << " " << std::endl; 
} 

allora non avrete alcun errore multiple definition of. Allo stesso modo, due diversi .cpp che chiamano la funzione con lo stesso modello (ad esempio incAndShow<int>()) condivideranno myStaticVar. È normale? Sto facendo questa domanda, perché mi affido a questa "caratteristica" (condividendo la variabile statica) e voglio essere sicuro che non è solo la mia implementazione a farlo.

+0

+1 Buona domanda.Ora ora sto implementando qualcosa che * "si basa su questa funzione" *. – Nawaz

risposta

27

Potete fidarvi di questo. L'ODR (One Rule Definition) dice alla 3.2/5 nello Standard, dove D sta per la funzione di modello non statico (di carattere corsivo da me)

Se D è un modello, ed è definito in più di una traduzione unità, quindi gli ultimi quattro requisiti dell'elenco sopra si applicano ai nomi dall'ambito di inclusione del modello utilizzato nella definizione del modello (14.6.3), e anche ai nomi dipendenti nel punto di istanziazione (14.6.2).Se le definizioni di D soddisfano tutti questi requisiti, il programma si comporterà come se esistesse un'unica definizione di D. Se le definizioni di D non soddisfano questi requisiti, il comportamento non è definito.

degli ultimi quattro requisiti, i due più importanti sono approssimativamente

  • ogni definizione di D è composto della stessa sequenza di token
  • nomi in ogni definizione fanno riferimento alle stesse cose ("entità")

Modifica

Immagino che questo da solo non sia sufficiente per garantire che le variabili statiche nelle diverse istanze siano tutte uguali. Quanto sopra garantisce solo che le molteplici definizioni del modello siano valide. Non dice qualcosa sulle specializzazioni generate da esso.

Questo è dove linkage calci. Se il nome di una specializzazione di modello di funzione (che è funzione) ha linkage esterno (3.5/4), quindi un nome che si riferisce a tale specializzazione si riferisce alla stessa funzione. Per un modello che è stato dichiarato statico, funzioni esemplificate da essa hanno collegamento interno, a causa della

entità generate da un modello di collegamento interno sono distinti da tutte le entità generate in altre unità di traduzione. -- 14/4

Un nome avente namespace portata (3.3.6) ha il collegamento interno se è il nome di [...] un oggetto, di riferimento, la funzione o modello di funzione che è esplicitamente dichiarate static -- 3.5/3

Se il template della funzione non è stato dichiarato con static, allora ha un link extern (che, a proposito, è anche la ragione per cui dobbiamo seguire l'ODR del tutto, altrimenti, D non sarebbe affatto definito per moltiplica!). Questo può essere derivato da 14/4 (insieme con 3.5/3)

Un modello di funzione non-membro può disporre di collegamento interno; qualsiasi altro nome di modello deve avere un collegamento esterno. -- 14/4.

Infine, arriviamo alla conclusione che un modello di funzione specializzazione generato da un modello di funzione con linkage esterno sé ha linkage esterno 3.5/4:

Un nome avente portata namespace ha linkage esterno se è il nome di [...] una funzione, se non ha il collegamento interno -- 3.5/4

E quando si ha il collegamento interno è stato spiegato da 3.5/3 per le funzioni fornite dal esplicito specializzazioni e 14/4 per le specializzazioni generate (istanze del modello). Poiché il nome del tuo modello ha un collegamento esterno, tutte le tue specializzazioni hanno un collegamento esterno: se usi il loro nome (incAndShow<T>) da diverse unità di traduzione, faranno riferimento alle stesse funzioni, il che significa che gli oggetti statici saranno gli stessi in ogni occasione.

5

Solo così capisco la tua domanda. Stai chiedendo se è normale che ogni versione della funzione basata su modelli abbia la propria istanza di myStaticVar. (Per esempio: incAndShow<int> vs intAndShow<float> La risposta è sì

tua altra domanda è, se due file includono l'intestazione che contiene la funzione di modello, sarà ancora condividere la variabile statica per un dato T. direi di sì..

0

I modelli vengono istanziati secondo necessità, il che significa che il compilatore (anche in questo caso il linker) si assicurerà che non ci siano più istanze dello stesso modello e solo quelle istanze di modelli che bisogno - nel tuo caso solo l'istanza incAndShow<int>() viene creata e nient'altro (altrimenti il ​​compilatore dovrebbe tentare di creare un'istanza per ogni tipo che non ha senso).

Quindi presumo che gli stessi metodi che usa per capire per quale tipo istanziare il modello gli impedisca di creare l'istanza due volte per lo stesso tipo, ad es. solo una istanza di incAndShow<int>()

Questo è diverso dal codice non modello.

-3
  • modelli saranno solo in realtà essere trasformati in codice una volta che sono istanziati (cioè utilizzati)
  • intestazioni non devono essere utilizzati per codice di implementazione, ma solo per le dichiarazioni
+0

persone, se ti preoccupi di mandarmi a valle, sarei felice di lasciare un commento, per quanto riguarda il problema - ovviamente la domanda originale era piuttosto vaga ... – none

+2

"le intestazioni non devono essere usate per il codice di implementazione, ma solo per le dichiarazioni " Non valido per i modelli. È necessario definire ("implementare") modelli nell'intestazione. l'esportazione era un modo per aggirare questo problema, che si è rivelato troppo difficile e non è generalmente implementato. – Suma

0

Sì, è "normale", ma qualsiasi cosa tu cerchi di ottenere con questa "funzionalità" è forse discutibile. Prova a spiegare perché vuoi usare la variabile statica locale, potremmo essere in grado di trovare un modo più pulito per farlo.

Il motivo per cui questo è normale dipende dal modo in cui le funzioni del modello sono compilate e collegate. Ogni unità di traduzione (le due .cpp nel tuo caso) ottiene la propria copia di incAndShow e quando il programma è collegato insieme, i due incAndShow saranno uniti in uno solo. Se dichiari la tua funzione regolare in linea nel file di intestazione, otterrai un effetto simile.

1

La differenza quando si crea il modello di funzione è che ha un collegamento esterno. Lo stesso incAndShow sarà accessibile da tutte le unità di traduzione.

Parafrasando dalla bozza di lavoro standard C++ N2798 (2008-10-04): 14 parte 4: un modello di funzione non membro può avere un collegamento interno, altri hanno sempre un collegamento esterno. 14.8 punto 2: ogni specializzazione avrà una propria copia della variabile statica.

Il modello di funzione dovrebbe avere un collegamento esterno a meno che non lo dichiari nello spazio dei nomi senza nome o qualcosa del genere. Quindi, per ogni T che usi con il tuo modello di funzione, dovresti ottenere una variabile statica usata per trasmettere il programma. In altre parole, è bene fare affidamento sul fatto di avere una sola variabile statica nel programma per ogni istanza del modello (una per T == int, una per T == breve, ecc.).

Per inciso, questo può portare a situazioni strane se si definisce incAndShow in modo diverso in diverse unità di traduzione. Ad esempio, se lo si definisce per incrementare in un file e il decremento in un altro file (senza specificare il collegamento interno inserendo la funzione nello spazio dei nomi senza nome) entrambi finiranno per condividere la stessa funzione, che verrà effettivamente scelta a caso al momento della compilazione (con g ++ dipende dall'ordine in cui i file oggetto sono dati sulla riga di comando).

-1

Prendete questo esempio che mostra il comportamento è assolutamente previsto:

#include <iostream> 

template <class T> class Some 
{ 
public: 
    static int stat; 
}; 

template<class T> 
int Some<T>::stat = 10; 

void main() 
{ 
    Some<int>::stat = 5; 
    std::cout << Some<int>::stat << std::endl; 
    std::cout << Some<char>::stat << std::endl; 
    std::cout << Some<float>::stat << std::endl; 
    std::cout << Some<long>::stat << std::endl; 
} 

Si ottiene: 5 10 10 10 10

Quanto sopra dimostra che il cambiamento di variabile statica è solo per il tipo "int" e, quindi, nella vostra caso non vedi alcun problema.

+3

-1: questo esempio dimostra membro della classe statico; la domanda è chiedere la variabile statica all'interno di una funzione. –

Problemi correlati