C++ template: The static member in a global object is not initialized

前端 未结 1 536
离开以前
离开以前 2021-02-07 22:27

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 special

1条回答
  •  谎友^
    谎友^ (楼主)
    2021-02-07 23:13

    In the first scenario, your program crashed before main started. It crashes inside the constructor of ta because it accesses tb which was not yet constructed. This is a form of the Static Initialization Order Fiasco

    The second scenario was successful because ta is inside main, and that guaranteed that tb which is non-local, was constructed before ta.

    The question is, why in the first scenario, ta was constructed before tb even though tb and ta were defined in the same compilation unit, with tb defined before ta?

    Knowing that tb is a template static data member, this quote from cppreference applies:

    Dynamic initialization

    After all static initialization is completed, dynamic initialization of non-local variables occurs in the following situations:

    1) Unordered dynamic initialization, which applies only to (static/thread-local) variable templates and (since C++11) class template static data members that aren't explicitly specialized. Initialization of such static variables is indeterminately sequenced with respect to all other dynamic initialization except if the program starts a thread before a variable is initialized, in which case its initialization is unsequenced (since C++17). Initialization of such thread-local variables is unsequenced with respect to all other dynamic initialization.

    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 before tb.

    Another quote from the same page says:

    Early dynamic initialization

    The compilers are allowed to initialize dynamically-initialized variables as part of static initialization (essentially, at compile time), if the following conditions are both true:

    1) the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization

    2) the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically. Because of the rule above, if initialization of some object o1 refers to an namespace-scope object o2, which potentially requires dynamic initialization, but is defined later in the same translation unit, it is unspecified whether the value of o2 used will be the value of the fully initialized o2 (because the compiler promoted initialization of o2 to compile time) or will be the value of o2 merely zero-initialized.

    The compiler has decided according to these rules to promote the initialization of ta before tb. 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:

    template
    class TA{
        //...
        static TB& getTB();
    };
    
    template
    TB& TA::getTB()
    { static TB tb("static-tb");
      return tb;
    }
    

    0 讨论(0)
提交回复
热议问题