A friend posed a question this evening as to why the following code should not be accepted as valid C++03:
This code is accepted by clang, but rejected by (at least) g++ 4.4-4.6 with the diagnostic:
error: no matching function for call to `fill(unsigned char*&, unsigned char*, Foo::<anonymous enum>)`
At first I blamed too many implicit conversion steps but, after a short discussion, the answer turned out to be quite simple: the enum
is anonymous, and std::fill
is a template whose third implicit argument here is an anonymous type… which is impossible in C++03.
If we pass the value as the int
that it was always destined to become (before eventually becoming a char
), we get:
Which behaves as one expects on all toolchains.
C++0x will accept anonymous types as template parameters but, for now, clang appears to be in error. At least it's filed. 🙂
Right or wrong, it's still trendy in some C++ circles to use the singleton pattern for defining a type that can only be instantiated at most once in an execution run. An example might be a god class that manages the top-level of your framework:
/** .. useful code here .. **/
static Kernel& getInstance() {
return instance;
}
private:
Kernel() {};
static Kernel instance;
};
Kernel Kernel::instance;
The constructor is private, so nobody can outright just instantiate a Kernel instance; in the meantime, the Kernel::getInstance()
function will give you access to the static member Kernel instance that's created once during static initialisation.
Problem
However, this can cause problems when you bring more static data into the mix. Consider the following example:
std::map m;
static Kernel& getInstance() {
return instance;
}
private:
Kernel() {};
static Kernel instance;
};
struct Utility {
Utility() {
Kernel::getInstance().m[0] = 1;
}
};
Kernel Kernel::instance;
Utility utility;
Because the Kernel instance is created before the Utility instance's constructor is invoked, this is safe.
But if we flip the static initialisation order of the two objects, then we're trying to use a data member inside Kernel that hasn't been initialised yet:
std::map m;
static Kernel& getInstance() {
return instance;
}
private:
Kernel() {};
static Kernel instance;
};
struct Utility {
Utility() {
Kernel::getInstance().m[0] = 1;
// ^ the map does not yet exist;
// the [] operation is UB and may
// very well segfault.
}
};
Utility utility;
Kernel Kernel::instance;
Worse, if your Utility and Kernel instances are defined in difference Translation Units (loosely corresponding to .cpp
source files in most projects), then their order of initialisation is completely non-guaranteed. It's pot luck as to whether your "simple" code will function at all.
Solution
It may seem contrived but this pattern has been seen in production code. To get around this issue when one static object depends on another static object, you can instead rely on the create-on-demand semantics of function-statics (an altogether different feature of the language):
std::map m;
static Kernel& getInstance() {
static Kernel instance;
// ^ this is guaranteed to be created when we
// first need it, and to never be re-created.
return instance;
}
private:
Kernel() {};
};
struct Utility {
Utility() {
Kernel::getInstance().m[0] = 1;
// ^ the Kernel instance and its map will be
// created by the first getInstance() call.
}
};
Utility utility;
In fact, in general it's best to try to avoid relying on static data members as these sorts of errors can go unnoticed for some time, and exerting control over C++'s static initialisation order is the sort of magic trickery best left to gremlins and hyperdimensional beings.
Update 18/12/2010:
I'm having to add an addendum here to address a typical counter-response to my advice above.
It's been contested a few times that you can retain the use of the static member instance by using indirection, as in the following code. However, I've annotated the code to demonstrate how using dynamic allocation and a static member pointer does not solve this problem:
static Kernel& getInstance() {
if (instance == NULL)
instance = new Kernel();
return *instance;
}
private:
Kernel() {};
static Kernel* instance;
};
struct Utility {
Utility() {
Kernel& k = Kernel::getInstance();
// ^ the instance pointer hasn't been
// initialised yet: it's just an arbitrary,
// invalid pointer.
// Kernel::getInstance() will compare
// it to NULL and then think that it has
// a valid instance, dereferencing an
// invalid pointer and pretending that
// it's got the Kernel instance.
}
};
Utility utility;
Kernel* Kernel::instance = 0;
Remember: always prefer a function-static implementation object for singletons!