I played a bit with this topic and came up with an alternative solution in case you use C++11. Consider the following:
class MyClass
{
public:
MyClass() :
expensiveObjectLazyAccess()
{
// Set initial behavior to initialize the expensive object when called.
expensiveObjectLazyAccess = [this]()
{
// Consider wrapping result in a shared_ptr if this is the owner of the expensive object.
auto result = std::shared_ptr<ExpensiveType>(CreateExpensiveObject());
// Maintain a local copy of the captured variable.
auto self = this;
// overwrite itself to a function which just returns the already initialized expensive object
// Note that all the captures of the lambda will be invalidated after this point, accessing them
// would result in undefined behavior. If the captured variables are needed after this they can be
// copied to local variable beforehand (i.e. self).
expensiveObjectLazyAccess = [result]() { return result.get(); };
// Initialization is done, call self again. I'm calling self->GetExpensiveObject() just to
// illustrate that it's safe to call method on local copy of this. Using this->GetExpensiveObject()
// would be undefined behavior since the reassignment above destroys the lambda captured
// variables. Alternatively I could just use:
// return result.get();
return self->GetExpensiveObject();
};
}
ExpensiveType* GetExpensiveObject() const
{
// Forward call to member function
return expensiveObjectLazyAccess();
}
private:
// hold a function returning the value instead of the value itself
std::function<ExpensiveType*()> expensiveObjectLazyAccess;
};
Main idea is to hold a function returning the expensive object as member instead of the object itself. In the constructor initialize with a function that does the following:
- Initializes the expensive object
- Replaces itself with a function which captures the already initialized object and just returns it.
- Returns the object.
What I like about this is that the initialization code is still written in the constructor (where I'd naturally put it if lazyness was not needed) even though it will only get executed when the first query for the expensive object happens.
A downside of this approach is that std::function reassigns itself within it's execution. Accessing any non static members (captures in case of using lambda's) after the reassignment would result in undefined behavior so this requires extra attention. Also this is kind of a hack since GetExpensiveObject() is const but it still modifies a member attribute on first call.
In production code I'd probably prefer to just make the member mutable as James Curran described. This way the public API of your class clearly states that the member is not considered part of the objects state, therefore it doesn't affect constness.
After a bit more thinking I figured that std::async with std::launch::deferred could also be used in combination with a std::shared_future in order to be able to retrieve the result multiple times. Here is the code:
class MyClass
{
public:
MyClass() :
deferredObj()
{
deferredObj = std::async(std::launch::deferred, []()
{
return std::shared_ptr<ExpensiveType>(CreateExpensiveObject());
});
}
const ExpensiveType* GetExpensiveObject() const
{
return deferredObj.get().get();
}
private:
std::shared_future<std::shared_ptr<ExpensiveType>> deferredObj;
};