Function-local static initialization during program exit

Viewed 295

What does the standard have to say about function-local static initialization during program exit? EDIT: For clarity, I mean a case as in the code example - the local static b is constructed after another static a is constructed(so supposedly b should be destroyed before a), but b is also constructed during a's destructor, so should it be destroyed immediately? after? UB?

I didn't manage to find any reference to this matter.

I'd like to know if such a case is UB, or should it have some defined behaviour?

The following code is an example of that:

struct B{};

void foo()
{
    static B b;
}

struct A
{
    ~A() { foo(); }
};

int main()
{
    static A a;
    
    return 0;
}

As you can see, the destructor of A would occur during program exit(since it has static storage), and it'll try to construct a B static instance.

I'm more interested in C++17 if it makes any difference in this subject.

2 Answers

I do not see a conflict here, given that foo() is only ever called from the destructor of A.

From [stmt.dcl]/5 [emphasis mine]:

A block-scope object with static or thread storage duration will be destroyed if and only if it was constructed. [ Note: [basic.start.term] describes the order in which block-scope objects with static and thread storage duration are destroyed. — end note ]

And, from [basic.start.term]/4 [emphasis mine]:

If a function contains a block-scope object of static or thread storage duration that has been destroyed and the function is called during the destruction of an object with static or thread storage duration, the program has undefined behavior if the flow of control passes through the definition of the previously destroyed block-scope object. Likewise, the behavior is undefined if the block-scope object is used indirectly (i.e., through a pointer) after its destruction.

As b in foo() is (in this particular example) not constructed until the destruction of a in main(), [basic.start.term]/4 will not be violated as b is yet to be destroyed at this point.

If foo() is invoked prior to the destruction of a in main(), it's a different story.

Since Object B was created by A's destructor, it will be destroyed before program exits. I added print statement in your code to make it look like this:

#include <iostream>

struct B
{
    ~B()
    {
        std::cout << "Called B's Destructor...!";
    }
};

void foo()
{
    static B b;
}

struct A
{
    ~A() {
        foo();
        std::cout << "Called A's Destructor...!\n";
    }
};

int main()
{
    static A a;
    
    return 0;
}

It prints the following:

Called A's Destructor...!
Called B's Destructor...!

The bottomline is, the static variables will be destroyed at the end of the program (local or global), and in the order it was created.

Related