Question

Doesn't a compiler have all the information it needs to generate a dependency tree of all globals and create a well defined and correct initialization order for them? I realize you could write a cyclic dependency with globals - make only that case undefined behavior - and the compiler could warn and maybe error about it.

Usually the reason for this sort of thing is that it would be burdensome to compiler makers or cause compilation to slow significantly. I have no metrics or evidence that indicates either of these wouldn't be true in this case, but my inclination is that neither would be true.

Was it helpful?

Solution

Hm, imagine the following setup, which is perfectly valid C++, but tricky to analyze:

// TU #1

bool c = coin();

// TU #2

extern bool c;
extern int b;
int a = c ? b : 10;

// TU #3

extern bool c;
extern int a;
int b = c ? 20 : a;

It is clear that TU #1 needs to be initialized first, but then what? The standard solution with references-to-statics allows you to write this code correctly with standard C++, but solving this by fixing the global initialization order seems tricky.

OTHER TIPS

The part the compiler can deal with is actually define: objects with static storage duration are constructed in the order their definition appears in the translation unit. The destruction order is just the reverse.

When it comes to ordering objects between translation units, the dependency group for objects is typically not explicitly represented. However, even if the dependencies were explicitly represnted, they wouldn't actually help much: on small projects the dependencies between objects with static storage duration can be managed relatively easy. Where things become interesting are large objects but these have a much higher chance to include initializations of the form

static T global = functionWhichMayuseTheword();

i.e., in the case where the ordering would be useful it is bound not to work.

There is a trivial way to make sure objects are constructed in time which is even thread-safe in C++ (it wasn't thread-safe in C++03 as this standard didn't mention any concept of threads in the first place): Use a function local static object and return a reference to it. The objects will be constructed upon demand but if there are dependencies between them this is generally acceptable:

static T& global() {
    static rc = someInitialization();
    return rc;
}

Given that there is a simple work-around and neither a proposal nor a working implementation demonstrating that the proposal does work, there is little interest to change the state of how global objects are initialized. Not to mention that improving the support for global objects seems as useful as making goto better.

I am not a compiler author so take what I say with a grain of salt. I think the reasons are as follows.

1) Desire the preserve the C model of separate compilation. Link time analysis is certainly allowed, but I suspect they did not want to make it required.

2) Meyers Singleton (especially now that it has been made thread-safe) provides a good enough alternative in that it is almost as easy to use as a global variable but provides the guarantees you are looking for.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top