Destruction order of statically initialized, non-literal objects
Asked Answered
B

2

11

A recent question drew my attention to the way that constexpr has changed in C++14. The new feature is that a non-local variable with static storage duration may be initialized in the static initialization phase if its initializer consists of a constexpr constructor, even if the type of the variable isn't a literal type. More precisely, the new wording in [basic.start.init] is:

A constant initializer for an object o is an expression that is a constant expression, except that it may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types [Note: such a class may have a non-trivial destructor — end note]. Constant initialization is performed [...] if an object with static or thread storage duration is initialized by a constructor call, and if the initialization full-expression is a constant initializer for the object [...]

The typical example is std::unique_ptr, which "should never be worse that hand-written":

std::unique_ptr<int> p;   // statically initialized by [unique.ptr.single.ctor],
                          // requires no code excution
int main()
{
    p = std::make_unique<int>(100);
}

// p is destroyed eventually

Prior to this addition, statically initialized variables were either of reference type or of literal object type, and therefore had trivial destructors. But now a statically initialized global variable can have a non-trivial destructor.

How is such a destructor call ordered with respect to the destructors of dynamically initialized global objects, with respect to other statically initialized ones, and how are the destructor calls sequenced?

Battik answered 29/11, 2014 at 0:9 Comment(2)
This isn't new to C++14; even the previous wording allows this. (std::mutex has a constexpr constructor so that it can take advantage of static initialization, for instance.)Louiselouisette
@T.C.: Thanks for the clarification!Battik
W
4

Consider

If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized.

and

If the completion of the constructor or dynamic initialization of an object with static storage duration is sequenced before that of another, the completion of the destructor of the second is sequenced before the initiation of the destructor of the first.

Now,

Static initialization shall be performed before any dynamic initialization takes place.

Clearly this answers the first question: Since p is guaranteed to be initialized before any dynamic initialization is performed, the destructor is called after any dynamically initialized object is destroyed.

Essentially the second question, i.e. what order the destructions of several statically initialized variables have, is reduced to the ordering of the initialization of these:

Dynamic initialization of a non-local variable with static storage duration is either ordered or unordered. Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization. Other non-local variables with static storage duration have ordered initialization.

The bold sentence includes all statically initialized objects that are not static data members of instantiated classes. They are ordered within one translation unit:

Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit.

So, to summarize:

  • Variables which are subject of static initialization and are not static data members of an instantiated class are destroyed in the reverse order of definition in a translation file.

  • ... those variables are always destroyed after any dynamically initialized object is destroyed.

However, despite possible argumentative mistakes, neither Clang nor GCC seem to implement it this way at the moment: Demo.

Wheelock answered 29/11, 2014 at 1:7 Comment(4)
Is this from the C++14 (FD)IS?Battik
@KerrekSB N4140, which apparently only contains editorial changes. Let me check for the compliance with the latest draft.Wheelock
@KerrekSB (Alternatively, provide a link to what document I should quote and check these quotes against)Wheelock
Yes, right, it's in N4140. Oddly, the bold phrase seems to have disappeared from head. How embarrassing, my entire question based on the wrong document. Thanks!Battik
C
2

[basic.start.term]/1 (N4140) says:

If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized.

As I understand, this means that for the purpose of determinig the order of destruction, all static initialization is treated as dynamic (ordered or unordered) and the destructors are called in reverse order of this initialization.

Coolth answered 29/11, 2014 at 0:21 Comment(5)
Yes, right, but for dynamic initialization there is a set of rules for ordering (ordered vs unordered). How do statically initialized objects fit into that ordering in view of this rule?Battik
You see, it says "Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definition". But that only helps if the initialization is ordered to begin with, which isn't the case for statically initialized variables.Battik
@KerrekSB I think it's quite clear (updated my answer), but to be honest I haven't tested if this really works as I think with current compilersCoolth
@Anton Where are you quoting from? Sometimes the document matters.Rosetta
@remyabel N4140, but in N3337 it's the same.Coolth

© 2022 - 2024 — McMap. All rights reserved.