7

Inspired by this question.

We know that global variables with non-constexpr initializers undergo two different "initializations":

  • First, the "static initialization", which zero-initializes them.
  • Second, the "dynamic initialization", which uses the user-provided initializer.

Which of those initializations starts the variable lifetime? [basic.life] is being surprisingly unhelpful:

The lifetime of an object ... begins when: ... its initialization (if any) is complete

I see several options:

  1. The last initialization starts the lifetime.
  2. The first initialization starts the lifetime.
  3. Each successive initialization destroys the existing object and creates a new one in its place.

(1) Would make the most sense, but it would make the static initialization of an object that will later be dynamically initialized mostly useless.

(2) Would have interesting effects. E.g. the static init order fiasco is suddenly not UB (by itself) anymore.

(3) Would be very weird.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 2
    I didn't think zero initialization started the lifetime of objects. At least for class types. I don't believe it calls any constructor, so you can't guarantee any invariances. I always interpreted it as zero initialization is just zeroing out the underlying representation before the actual initialization happens. – François Andrieux Jun 15 '21 at 18:08
  • @FrançoisAndrieux Regardless of whether the constructor is called. See [dcl.init.general] p19: *An object whose initialization has completed is deemed to be constructed, even if the object is of non-class type or no constructor of the object's class is invoked for the initialization.* – xmh0511 Aug 10 '21 at 05:59

3 Answers3

4

Indeed, the standard is vague by not specifying how exactly the quoted rule "initialization (if any) is complete" applies to multiple stages of initialization, each of which being referred to as "initialization". And this leaves room for multiple interpretations.

This footnote (non-normative) implies that the lifetime begins only after dynamic initialisation is complete:

[basic.life]

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated 26 ...

26 For example, before the dynamic initialization of an object with static storage duration ([basic.start.dynamic]).

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thanks, this footnote leaves little room for doubt. I've posted [an answer](https://stackoverflow.com/a/67991829/2752075) to the original question based on this one. – HolyBlackCat Jun 15 '21 at 18:44
  • 3
    AFAIR from reflector discussions, there was no intention to «break» access to objects before dynamic initialization. But explaining which initialization is enough to start the lifetime prolly needs a core issue. – Language Lawyer Jun 15 '21 at 19:09
4

As the other answer makes its argument solely on a non-normative footnote, I will offer a counter-argument based on non-normative note. Not that I have any belief in this interpretation over the other, but I think the standard is in non-normative conflict here, at best.

Note 2 of [basic.start.static]/3 gives an example of unspecified, not undefined, behavior where an implementor (adhering to [basic.start.static]/3) is permitted to perform initialization of a static/thread storage duration variable even if it is not required to be initialized during static initialization (could be during dynamic initialization). The key here being the note's example of why the value of obj1 is unspecified: namely that the value of the object which is used to initialize it may be "fully initialized" or "merely zero-initialized":

[Note 2: As a consequence, if the initialization of an object obj1 refers to an object obj2 potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically initialized) or will be the value of obj2 merely zero-initialized. For example,

inline double fd() { return 1.0; }
extern double d1;
double d2 = d1;     // unspecified:
                    // either statically initialized to 0.0 or
                    // dynamically initialized to 0.0 if d1 is
                    // dynamically initialized, or 1.0 otherwise
double d1 = fd();   // either initialized statically or dynamically to 1.0

— end note]

Particularly the comment from the example code snippet:

[d2] either statically initialized to 0.0 or dynamically initialized to 0.0 if d1 is dynamically initialized, or 1.0 otherwise.

Implying that the "otherwise" case, namely where d1 is (dynamically) initialized from the "merely zero-initialized" d2 is not undefined.

dfrib
  • 70,367
  • 12
  • 127
  • 192
1

I would say, the dynamic initialization begins the lifetime of that object. Since the lifetime is a property defined as that

[basic.life#1]

The lifetime of an object or reference is a runtime property of the object or reference.

Zero-initialization could be said that occurs at a compile-time that is the reason why static initialization requires a constant expression as its initialization, which could be evaluated at compile-time.

The initialization in the normative rule you cited arguably refers to all dynamic initializations that can start the lifetime of an object(at runtime).

xmh0511
  • 7,010
  • 1
  • 9
  • 36