5

In fact, the problem comes from the words in the standard draft N4582:

[basic.start.static/3] An implementation is permitted to perform the initialization of a variable with static or thread storage duration as a static initialization even if such initialization is not required to be done statically, provided that

— the dynamic version of the initialization does not change the value of any other object of static or thread storage duration prior to its initialization, and

— 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.

Do these words mean that if the two conditions are satisfied, a non-local variable of class type may be fully initialized statically (zero-initialized) so that its constructor is not called (since the dynamic version, initializing by calling a constructor, may be replaced by a static version)?

Community
  • 1
  • 1
xskxzr
  • 12,442
  • 12
  • 37
  • 77
  • If a class has a user defined constructor, it will definitely be called. Whether it is zero initialized before the call to the constructor is not clear to me. – R Sahu Jun 10 '16 at 21:51
  • This is only my interpretation, but I doubt that zero-initialisation before the "call" of the constructor is required - the purpose of a constructor, among other things, is to initialise the memory occupied by an object. The rules of static and dynamic initialisation affect the order of construction of distinct objects. The rules are recursive though, since objects have bases and contain members that are initialised before the constructor call. A non-class member (e.g. an `int`) may well be zero-initialised in that process. – Peter Jun 10 '16 at 22:13
  • @RSahu Thanks for your answer, but my point is that why the constructor will definitely be called? Calling a constructor is a behavior in dynamic initialization, but as these words in the standard say, dynamic initialization is permitted to be implemented as a static initialization. – xskxzr Jun 11 '16 at 06:32
  • I don't know how an implementation can guarantee that a user defined constructor will not change the value of another object of static or thread storage duration. – R Sahu Jun 11 '16 at 17:57

1 Answers1

3

Static initialization is performed during compilation/linking. The compiler/linker assigns a location to the variable in the static memory and fills it with the correct bytes (the bytes don't need to be all zeros). When the program starts, those regions of the static memory are loaded from the program's binary file and no further initialization is required.

Examples:

namespace A {
    // statically zero-initialized
    int a;
    char buf1[10];

    // non-zero initialized
    int b = 1;
    char date_format[] = "YYYY-MM-DD";
}

Unlike static initialization, dynamic initialization requires running some code after program start-up to set thus initialized variables to their initial state. The code that needs to be run doesn't need to be a constructor call.

Examples:

namespace B {
    int a = strlen(A::date_format);   (1)
    int b = ++a;                      (2)

    time_t t = time();                (3)

    struct C {
        int i;

        C() : i(123) {}
    };

    C c;                              (4)

    double s = std::sqrt(2);          (5)
}

Now, the C++ standard allows the compiler to perform the computations that would be carried out during dynamic initialization, provided that those computations do not have side effects. Besides, those computations must not depend on external environment. In the above example:

(1) can be performed statically since strlen() doesn't have any side-effects.

(2) must stay dynamic since it mutates a.

(3) must stay dynamic since it depends on external environment/makes system calls.

(4) can be performed statically.

(5) is a little tricky, since floating point computation depends on the state of the FPU (namely, rounding mode). If the compiler is told not to treat floating point arithmetic that seriously, then it can be performed statically.

Leon
  • 31,443
  • 4
  • 72
  • 97
  • Thanks for your answer. So in your example, if c is initialized statically, will the constructor be skipped while c.i is set to be 123 directly? If so, according to the first paragraph in [class.cdtor]: "For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior", any further access to c.i in your example will lead to undefined behavior though it has a clear value 123, how incredible! Or is there some mistakes in the above analysis? – xskxzr Jun 11 '16 at 08:28
  • For the example code, the constructor can be considered to have executed, since its only effect is to set `c.i` to `123`. The fact that the compiler somewhat rearranged how that was done is just an implementation detail. – Leon Jun 11 '16 at 08:40
  • What if side effects exist? [basic.start.static/3] seems to put no requirement on side effects. – xskxzr Jun 11 '16 at 09:07
  • "does not change the value of any other object" is how it refers to side-effects – Leon Jun 11 '16 at 10:03