Static locals are guaranteed to be instantiated at first use by the C++ standard. However, I'm wondering what happens if I access a static local object while it is beeing constructed. I assume that this is UB. But what are the best practices to avoid this in the following situation?
A problem situation
The Meyers Singleton pattern uses a static local in a static getInstance()
method to construct the object on the first use. Now if the constructor (directly or indireclty) calls getInstance()
again, we face
a situation where the static initialization is not yet completed. Here is a minimal example, that illustrates the problem situation:
class StaticLocal {
private:
StaticLocal() {
// Indirectly calls getInstance()
parseConfig();
}
StaticLocal(const StaticLocal&) = delete;
StaticLocal &operator=(const StaticLocal &) = delete;
void parseConfig() {
int d = StaticLocal::getInstance()->getData();
}
int getData() {
return 1;
}
public:
static StaticLocal *getInstance() {
static StaticLocal inst_;
return &inst_;
}
void doIt() {};
};
int main()
{
StaticLocal::getInstance()->doIt();
return 0;
}
In VS2010, this works without problems, but VS2015 deadlocks.
For this simple, reduced situation, the obvious solution is to direclty call getData()
, without calling getInstance()
again. However, in more complex scenarios (as my actual situation), this solution is not feasible.
Attempting a solution
If we change the getInstance()
method to work on a static local pointer like this (and thus abandon the Meyers Singleton pattern):
static StaticLocal *getInstance() {
static StaticLocal *inst_ = nullptr;
if (!inst_) inst_ = new StaticLocal;
return inst_;
}
It is clear that we get an endless recursion. inst_
is nullptr
on the first invokation, so we call the constructor with new StaticLocal
. At this point, inst_
is still nullptr
as it will only get assigned when
the constructor finishes. However, the constructor will call getInstance()
again, finding a nullptr
in inst_
, and thus call the constructor again. And again, and again, ...
A possible solution is to move the constructor's body into the getInstance()
:
StaticLocal() { /* do nothing */ }
static StaticLocal *getInstance() {
static StaticLocal *inst_ = nullptr;
if (!inst_) {
inst_ = new StaticLocal;
inst_->parseConfig();
}
return inst_;
}
This will work. However, I'm not happy with this situation, as a constructor should, well, construct a complete object. It is debateable if this situation can be made an exception, as it is a singleton. However, I dislike it.
But what's more, what if the class has a non-trivial destructor?
~StaticLocal() { /* Important Cleanup */ }
In the above situation, the destructor is never called. We loose RAII and thus one important distinguishing feature of C++! We are in a world like Java or C#...
So we could wrap our singleton in some sort of smart pointer:
static StaticLocal *getInstance() {
static std::unique_ptr<StaticLocal> inst_;
if (!inst_) {
inst_.reset(new StaticLocal);
inst_->parseConfig();
}
return inst_.get();
}
This will correctly call the destructor on program exit. But it forces us to make the destructor public.
At this point, I feel I'm doing the compiler's job...
Back to the original question
Is this situation really undefined behaviour? Or is it a compiler bug in VS2015?
What is the best solution to such a situation, prefably without dropping a full constructor, and RAII?