Allan
Allan

Reputation: 4720

Only construct if non-static

I have an intrusive linked list which is used in various contexts - sometime is is used as a global static variable, and sometime I use it as a normal variable.

When it is used as a global static variable, then I would like if I could avoid to have the constructor running - as all members will be initialized to zero anyway.

When it is used as a normal variable, then I would like to have a constructor which initialize the members (two pointers) to null.

The motivation for this is that I have different compilation units and have no control on the order of which static constructors are being invoked. Some of the constructors in other compilation unites is "hooking" into the global lists, and the problem is that one constructor may use the list before it is constructed.

The idea was that if the list did not need to be constructed - then there was not problem with initialization order.

Upvotes: 1

Views: 82

Answers (1)

Jonathan Wakely
Jonathan Wakely

Reputation: 171393

Just write the default constructor normally but make it constexpr.

struct List
{
  constexpr List() : head(nullptr), tail(nullptr) { }
  ...
};

When you define a List object with static storage duration (i.e. a global) the compiler ensures the object is not initialized dynamically, so it is guaranteed to happen before any other constructors need it during the dynamic initialization phase.

When you declare a variable with automatic or dynamic storage duration (i.e. as a local variable or on the heap) the constructor will run normally and set the two members.

This is exactly the reason why types such as std::mutex have constexpr constructors, to ensure they are initialized before anything can try to use them.

If you need a C++03 solution, either make the global a local static that can be accessed (and initialized) as needed via a function:

inline List& global_list()
{
  static List list;
  return list;
}

or you could resort to a klugey solution involving a second constructor:

struct List
{
  struct no_init_t { };
  static no_init_t no_init;
  List() : head(0), tail(0) { }
  List(no_init_t) { }
  ...
};

List global_list(List::no_init);

This second constructor intentionally does no initialization, relying on the fact that the members of the global will initially be zero anyway. This means that if code in another translation unit accesses the list before its lifetime starts (which is technically undefined behaviour) it finds the members with zero values and can add to the list, and if the no_init list constructor runs later it won't zero out the variables and lose the previously added data. This is ugly, but should work. Obviously the no_init constructor should only be used for globals, so this solution is more error-prone.

Upvotes: 4

Related Questions