Newsgroup: it.comp.lang.c++
Titolo: ordine di inizializzazione variabili statiche in un classe

Nel seguente esempio:
#include <iostream>
struct A
{
   A() { std::cout << "A::A()\n"; }
};
struct B
{
   B() { std::cout << "B::B()\n"; }
};
struct foo
{
   static A a;
   static B b;
   foo() { std::cout << "foo::foo()\n"; }
};
int main()
{
   foo f;
}
l'ordine di inizializzazione di a e b è implementation specified?
Nel caso in cui definisco foo::a e foo::b in un modulo separato, l'ordine
di inizializzazione è dato dall'ordine in cui le definisco?
Ad esempio con il BCC 5.5 ed il DJGPP se definisco in un file separato
B foo::b;
A foo::a;
l'output del programma è:
B::B()
A::A()
foo::foo()
I (grossi) problemi sorgono quando A e B sono classi template.
Infatti in questo caso l'output del programma è:
foo::foo()
Che cosa significa?
Se ora in A e B aggiungo un metodo
    void init() {  }
e nel costruttore di foo faccio
    foo::foo() { std::cout << "foo::foo()\n"; a.init(); b.init(); }
l'output è:
A::A()
B::B()
foo::foo()
(ossia ora l'ordine di inizializzazione è stabilito dall'ordine di utilizzo di a
e b)
Tutto ciò mi lascia alquanto confuso.
Qualcuno mi potrebbe dare una spiegazione esauriente?
Saluti e grazie   Manlio Perillo

 Qualcuno mi potrebbe dare una spiegazione esauriente?
IMHO o c'e' qualche cosa che non va nel tuo codice (la parte che non hai
postato) o qualche cosa di strano nei compilatori. Oppure c'e' una regola che
non conosco relativa all'inizializzazione delle statiche templated. Prima di
sfogliare lo standard prova a verificare il codice (magari posta).
--
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556


Hai ragione. Ho fatto io confusione con il mio codice.
L'ho sistemato un poco meglio ed in effetti l'inizializzazione dipende
dall'ordine in cui si definiscono i membri statici (nella stessa TU ovviamente).
La mia confusione nasce da un programmimo cui sto lavorando.
Ho una classe (template) con un membro statico template.
Quando uso questa classe stand-alone non ci sono problemi
con le inizializzazioni.
I problemi sono arrivati quando ho usato questa classe come membro
statico di un'altra classe...non mi ci raccapezzo più.
Counque sto cercando di analizzare meglio l'esecuzione del mio programma.
Saluti  Manlio Perillo


Ho trovato un esempio che mi aiuta a spiegare il problema:
#include <iostream>
template<class T> struct A
{
   A() { std::cout << "A::A()\n"; }
   void init() { std::cout << "A::init()\n"; }
};
template<class T> struct B
{
   static A<T> a;
   B()
   {
      std::cout << "B::B()\n";
      a.init();
   }
   void init() { std::cout << "B::init()\n"; }
};
template<class T> struct foo
{
   static B<T> b;
   foo()
   {
      std::cout << "foo::foo()\n";
      b.init();
   }
   void init() { std::cout << "foo::init()\n"; }
};
int main()
{
   #ifdef ALLOCA_FOO
   foo<int> f;
   f.init();
   #else
   B<int> b;
   b.init();
   #endif
}
// l'ordine di definizione non conta
template<class T> B<T> foo<T>::b;
template<class T> A<T> B<T>::a;
/***********************************************************/
Con ALLOCA_FOO non definita l'output è:
A::A()
B::B()
A::init()
B::init()
altrimenti si ha:
B::B()
A::init()
A::A()
foo::foo()
B::init()
foo::init()
Il problema è che sto accedendo ad a prima che questa sia stata inizializzata!
Lo Standard cosa dice a riguardo?
Io ottengo questo comportamento sia con il BCC 5.5 che con il DJGPP.
Esiste una tecnica per risolvere questo problema?


Che cosa significa?
Non sono un esperto di template e quindi potrei spararle grosse...
comunque secondo me bisogna distinguere due casi:
 foo e' un template a sua volta
   in questo caso credo che se A e B non vengono utilizzate
   esplicitamente il compilatore non sia tenuto ad
   inizializzarle, anzi, non debba proprio farlo.
   In una classe template solo i metodi che vengono
   utilizzati devono essere compilati, gli altri no (per
   mantenere il programma compilabile anche se alcuni membri
   che non usi di un template non sarebbero compilabili).
   Un caso classico e' una std::list di elementi non possono
   essere confrontati per un sort; gli errori di compilazione
   li hai solo se chiami ad esempio merge(). Se nessuno chiama
   metodi che non possono essere compilati quella istanziazione
   di template deve essere ammessa anche se incompleta.
   Sulle carte non ufficiali in mio possesso questo
   comportamento e' in parte descritto in 14.7.1 (implicit
   instantiation) dove viene fatto in particolare riferimento
   ad eventuali dati statici della classe template.
   (i membri non statici sono invece implicitamente riferiti
   nel costruttore e quindi vengono sempre istanziati quando
   crei un oggetto di quell'istanza di template)
 foo non e' un template
   in questo caso credo che A e B debbano necessariamente
   essere istanziate anche se non usate altrove.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 Esiste una tecnica per risolvere questo problema?
Al momento lo standard non definisce l'ordine in cui devono avvenire le
inizializzazioni. Il problema lo hai perche' il codice di inizializzazione di B
accede alla statica A. Se crei delle B solo in codice eseguito dopo il main, il
problema non esiste. Ma se esiste una B che e' statica a sua volta, come nel
caso di foo, allora non c'e' al momento nessuna garanzia dell'ordine in cui sono
inizializzate le due statiche. Nota che nel caso dei template il concetto di "TU
in cui si trova la definizione della statica" e' piuttosto "fuzzy" in quanto
tale definizione sta di fatto in un header sotto forma di template e pertanto
sta in tutte le TU che istanziano (o specializzano, se preferisci) il template
stesso. Inoltre dato che i template non sono specializzati dal tuo codice ma dal
compilatore non e' semplice controllare esattamente chi viene specializzato
prima e hi dopo. In ogni caso, anche se avessi questo controllo (per esempio
istanziando esplicitamente le statiche che ti interessano), non esiste nello
standard una garanzia in merito. Ho verificato nei report attivi e ne parla
l'issue 270 che e' in stato di review (cioe' lo stanno discutendo proprio
adesso). Al momento la proposta di modifica dello standard prevede che con il
tuo codice l'inizializzazione resti indefinita ma che instanziando
esplicitamente le statiche tu possa definirla (sempre che io abbia capito bene
il testo, l'ho letto solo di sfuggita: sai com'e' quando rispondi alle NG e hai
poco tempo). Per maggiori info prova a vedere
http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#270



Mi rispondo da solo.
Ho risolto il problema usando al posto di variabili statiche
delle funzioni statiche aventi variabili statiche locali
static A<T>& a()
{
    static A<T> a_;
    return a_;
}
Ora il mio programmino va bene.
Il problema resta però aperto.
E' vero che le variabili statiche di una classe sono normali
funzioni globali, ma il fatto che nel body del costruttore
non siano state ancora inizializzate mi lascia perplesso.
Saluti   Manlio Perillo

 non siano state ancora inizializzate mi lascia perplesso.
Che il problema sia aperto e' fuori dubbio (vedi mio altro messaggio).
Pero' non ho capito cosa intendi. In che senso una variabile statica di classe
e' una normale funzione globale?
Il fatto poi che nel body di un costruttore una statica non sia inizializzata
non sorprende ... se il costruttore e' richiamato per inizializzare una statica.
Viceversa sarebbe sorprendente se tu potessi eseguire un body dopo l'ingresso
nel main e quel body facesse riferimento ad una statica non ancora
inizializzata.



In effetti con il BCC 5.5 accade proprio questo.
Basta aggiungere
    template class B<int>;  // membro statico di foo
prima di main.
Tuttavia l'esplicita instantazione rende l'uso dei template
meno flessibile e 'comodo'.
Grazie per l'info.
Ciao  Manlio Perillo

 inizializzata.
Qui siamo d'accordo.
Comunque, come ho anche scritto in un altro messaggio, ho risolto il problema
usando funzioni statiche contenenti variabili locali statiche.
Saluti  Manlio Perillo

 variabili (anche se statiche) non ancora inizializzate.
Mumble. Come faresti ad evitare che tutti i costruttori vengano eseguiti solo
dopo l'inizializzazione delle statiche, se servono anche a questo? Per esempio
in questo caso come faresti:
// .hpp
class A;
class B {
public:
    B();
    static A& a;
};
class A {
public:
    A();
    static B& b;
};
// .cpp
A& B::a=A();
B& A::b=B();



Semplice disattenzione.
Nel libro parlava di funzioni statiche ed io ho riportato quello che
avavo letto.
Per le varabili statiche dovrebbe essere la stessa cosa.

Esempio interressante.
Comunque avevo scritto che la cosa non mi piace.
E' ben differente dal dire che non deve essere così.
Saluti  Manlio Perillo