I have a piece of simple C++ code, in which I defined a template and a global object by specializing the template. The object constructor accesses a static member in the specialized template. But it turns out the static member is not initialized at that point. But for a local object (defined in the body of a function), it works. I'm confused...
My c++ compiler is: g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
/////////////////////////
template<typename T>
class TB{
public:
const char *_name;
TB(const char * str):_name(str){
cout << "constructor is called:" << _name << endl;
};
virtual ~TB(){
cout << "destructor is called:" << _name << endl;
};
};
template<typename T>
class TA{
public:
const char *_name;
TA(const char * str):_name(str){
cout << "constructor is called:" << _name << endl;
cout << tb._name <<endl;
};
virtual ~TA(){
cout << "destructor is called:" << _name << endl;
};
static TB<T> tb;
};
template<typename T>
TB<T> TA<T>::tb("static-tb");
TA<int> ta("global-ta");
int main(int argc,char ** argv){
cout << "program started." << endl;
cout << "program stopped." << endl;
return 0;
}
/////////////////////////
// OUTPUT:
constructor is called:global-ta
// yes, only such a single line.
If I put the definition of ta in main() like the following, it works.
int main(int argc,char ** argv){
cout << "program started." << endl;
TA<int> ta("local-ta");
cout << "program stopped." << endl;
return 0;
}
/////////////////////
// OUTPUT:
constructor is called:static-tb
program started.
constructor is called:local-ta
static-tb
program stopped.
destructor is called:local-ta
destructor is called:static-tb
// end of output
In the first scenario, your program crashed before main started. It crashes inside the constructor of
ta
because it accessestb
which was not yet constructed. This is a form of the Static Initialization Order FiascoThe second scenario was successful because
ta
is insidemain
, and that guaranteed thattb
which is non-local, was constructed beforeta
.The question is, why in the first scenario,
ta
was constructed beforetb
even thoughtb
andta
were defined in the same compilation unit, withtb
defined beforeta
?Knowing that
tb
is a template static data member, this quote from cppreference applies:So there's no sequence guaranteed here! Since
ta
is a static variable with an explicit template specialization, the compiler was allowed to initialize it beforetb
.Another quote from the same page says:
The compiler has decided according to these rules to promote the initialization of
ta
beforetb
. Whether it was promoted to static initialization is not clear, but in any case, it seems pretty clear that the sequence of initialization is not guaranteed when it comes to variable templates and static templates members, due to the first quote and the promotion rules of the second quote.Solution
To ensure that
tb
is initialized before it is used, the simplest is to put it inside a wrapper function. I think that this should be somehow a rule of thumb when dealing with static templates members: