2015-07-13 40 views
6

Ecco un esempio minimo:inizializzazione di un membro statico all'interno di un modello di

#include <iostream> 

struct B { 
    B() { x = 42; } 
    static int x; 
}; 

int B::x; 

template <int N> 
struct A { 
    int foo() { return b.x; } 
    static B b; 
}; 

template<int N> 
B A<N>::b; 

//template struct A<2>; // explicit instantiation with N = 2 (!) 

int main(int argc, char **argv) { 
    std::cout << A<1>().foo() << std::endl; 
    return 0; 
} 

Questo programma scrive 42 con g ++ 4.9.2, ma scrive 0 utilizzando Visual Studio 2015 RC. Inoltre, se disattengo l'istanziazione esplicita, VS2015RC fornisce anche 42, il che è piuttosto interessante, poiché il parametro template qui è diverso da quello utilizzato nella funzione main.

È un bug? Suppongo che g ++ sia corretto, in quanto esiste un riferimento aall'interno di foo, pertanto è necessario chiamare il costruttore di B.


EDIT: C'è una soluzione semplice - se c'è una variabile non statico in B, a cui fa riferimento A, VS2015RC compilerà correttamente:

// ... 

struct B { 
    B() { x = 42; } 
    static int x; 
    int y;       // <- non-static variable 
}; 

// ... 

template <int N> 
struct A { 
    int foo() { b.y; return b.x; } // <- reference to b.y 
    static B b; 
}; 

Questo sembra funzionare, anche sebbene b.y, come una dichiarazione, è ovviamente NOP.

+0

possibilmente correlati: http://stackoverflow.com/questions/3035422/static-initialization-order-fiasco – NathanOliver

risposta

6

Da [basic.start.init]:

variabili con durata statica stoccaggio (3.7.1) o durata di conservazione filo (3.7.2) sono inizializzati a zero (8.5) prima di qualsiasi altra l'inizializzazione ha luogo. Un inizializzatore costante per un oggetto o è un'espressione che è un'espressione costante , eccetto per il fatto che può anche richiamare costruttori constexpr per o ei suoi sottooggetti anche se tali oggetti sono di tipi di classi non letterali. [...]

Insieme, l'inizializzazione zero e l'inizializzazione costante sono chiamate inizializzazione statica; tutte le altre inizializzazioni sono l'inizializzazione dinamica . L'inizializzazione statica deve essere eseguita prima di qualsiasi inizializzazione dinamica.

Nel nostro caso, b viene inizializzato in modo statico ma dinamico b.x è inizializzato (il costruttore non è constexpr). Ma abbiamo anche:

Si tratta di implementazione definita se l'inizializzazione dinamica di una variabile non locale con statico stoccaggio durata è fatto prima della prima istruzione del principale. Se l'inizializzazione viene posticipata ad un certo punto nel tempo dopo la prima dichiarazione di main, deve avvenire prima del primo uso odr (3.2) di qualsiasi funzione o variabile definita nella stessa unità di traduzione della variabile da inizializzare.

mezzi Odr utilizzati, da [basic.def.odr]:

A x variabile il cui nome appare come espressione ex potenzialmente valutati è ODR-usato da ex meno applicando lvalue conversione da -valore (4.1) a x produce un'espressione costante (5.20) che non richiama alcuna funzione non banale e, se [...]

Ma valutare b.x non produce un'espressione costante, così possiamo fermare lì - b.x è ODR-usato da A<N>::foo(), che è anche il primo ODR-uso. Pertanto, anche se l'inizializzazione non deve verificarsi prima di main(), è necessario che si verifichi prima di foo(). Quindi se ottieni 0, si tratta di un errore del compilatore.

+0

Grazie, mi sento rassicurato :) Tempo di presentare un bug report ... – vukung

-2

Sarei propenso a scrivere il codice come questo:

struct B { 
    B() {} 
    static int x; 
}; 

int B::x = 42; 

Dopo tutto, lo statico (x) è definita (e quindi deve essere inizializzata) nell'ultima riga. Mettere l'inizializzazione all'interno del costruttore di B significa che la statica x (ce n'è solo una!) Sarebbe re-inizializzata ogni volta che si costruisce un B. C'è una statica, si dovrebbe inizializzare solo una volta.

+0

Come fa la domanda a questo indirizzo ? – Barry

+0

Il bug è nel codice del poster. Completamente imprudente per inizializzare ripetutamente una statica nel costruttore. –

+0

Questo è un esempio minimo. Il codice reale che sto osservando ha un'inizializzazione complessa che coinvolge due array statici e la metaprogrammazione del modello. Inoltre, la statica non viene inizializzata ripetutamente, poiché esiste una sola istanza di B. – vukung

Problemi correlati