You could do something like this:
class Singleton
{
private:
static std::unique_ptr<Singleton>& getObject()
{
static std::unique_ptr<Singleton> instance;
return instance;
}
Singleton(int foo);
public:
static void Init(int foo)
{
auto& instance = getObject();
if (instance) throw std::runtime_error("aleady inited");
instance.reset(new Singleton(foo));
}
static Singleton& getInstance()
{
auto& instance = getObject();
if (!instance) throw std::runtime_error("not inited");
return *instance;
}
};
Note that this isn't thread safe and will have undefined behaviour if multiple threads call Init
or a thread calls getInstance
whilst another is calling Init
.
If your parameters could be replaced by template arguments then you could do it this way instead:
template <int foo>
class SingletonImpl
{
private:
SingletonImpl(int f);
public:
static SingletonImpl<foo>& getInstance()
{
static SingletonImpl<foo> instance(foo);
return instance;
}
};
using Singleton = SingletonImpl<10>;
The best solution is probably to separate initialisation and construction:
class Singleton
{
private:
std::atomic<bool> initialised;
Singleton()
: initialised(false)
{
}
Singleton& instanceImpl()
{
static Singleton singleton;
return singleton;
}
public:
void Init(int foo)
{
auto& instance = instanceImpl();
if (instance.initialised) throw std::runtime_error("already inited");
instance.initialised = true;
}
Singleton& getInstance()
{
auto& instance = instanceImpl();
if (!instance.initialised) throw std::runtime_error("not inited");
return instance;
}
};