c++language-lawyerstatic-initializationatexitinitialization-order

std::atexit ordering when called from a global object's constructor


cppreference says about std::atexit :

The functions may be called concurrently with the destruction of the objects with static storage duration and with each other, maintaining the guarantee that if registration of A was sequenced-before the registration of B, then the call to B is sequenced-before the call to A, same applies to the sequencing between static object constructors and calls to atexit

I understand that passage to mean that, if std::atexit is called during static initialization, the registered function will be called during the destruction of static objects just before the destruction of the static object which was last initialized when the std::atexit that registered the function was called. I also interpret "may be called concurrently" to mean the calls can occur between static object destructions as opposed to the multi-threaded interpretation of the word.

What I'm wondering is whether a object is considered as initialized (in the context of this ordering) when it's initialization begins or when it completes. I wrote a short test to test this :

#include <cstdlib>
#include <iostream>

struct foo
{
    foo() 
    {
        std::cout << "ctor\n";
        std::atexit([]() { std::cout << "atexit\n"; });
    }
    ~foo()
    {
        std::cout << "dtor\n";
    }
};

foo my_foo;

int main()
{
    return 0;
}

The output I get is (http://cpp.sh/3bllu) :

ctor
dtor
atexit

This leads me to believe that my_foo is not considered to be initialized in this context until it's construction finishes. In other words, the function is considered to have been registered before my_foo was initialized so the registered function executes after my_foo's destruction.

I can't seem to find anything that would guarantee this behavior and I'm not even entirely sure my initial interpretation of the cited passage is correct. Is the behavior I've described something that I can rely on or is it implementation defined or even undefined behavior?


Solution

  • The call to the destructor will happen before the call to the function passed to atexit. From [basic.start.term], p5:

    If a call to std::atexit strongly happens before the completion of the initialization of an object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to std::atexit.