user1235183 user1235183 - 14 hours ago 2
C++ Question

Destruction Order of Meyers Singletons

To the following code:

class C {
public:
static C& Instance() {
static C c;
return c;
}

~C(){std::cout << "c destructed\n";}
private:
C(){}
};

class D{//similar to C but prints `d destructed` on destruction
//...

int main()
{
auto c = C::Instance();
auto d = D::Instance();
}
//outputs (with gcc)
//d destructed
//c destructed
//d destructed
//c destructed


I have a couple of questions:


  1. Is the order of destruction calls well defined? (even if class C and D are defined in different source-files)

  2. If it is well defined is this behaviour portable?


Answer

The point of this construct is to impose a construction order (and thus a destruction order).

Construction

Since these are local static variables, the order of construction is determined by the order in which their respective Instance functions are called for the first time.

Since that is done in main, the construction order is fully specified.

The only way to make the order unspecified is if you use them in static initialisation in different translation units, for instance if one has

C& the_c = C::Instance();

and the other has

D& the_d = D::Instance();

Destruction

The destruction of objects with static storage is the reverse of the order of construction.

3.6.3, Termination, paragraph 1:

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.

So the destruction order is fully specified by the construction order.

Note that this singleton construct is well specified even if one of them depends on the other, regardless of translation unit.

That is, this is perfectly safe, and it doesn't matter where it's defined:

class C {
public:    
    static C& Instance() {
       static C c(D::Instance());
       return c;
    }

   ~C(){ m_d.doSomething(); } // Yes, this is safe.
private:
   C(D& d) : m_d(d) { m_d.doSomething(); } // Yes, this is safe.
   D& m_d;
};