today at work i was refactoring simple piece of code – a c-tor:
struct Sth { SomeClass(): name_("sth"), { factory_.add("type", builderFunction); } std::string name_; Factory factory_; }; // ... Sth sth; Producer p(sth.name_, sth.factory_);
but due to different separation of concerns, after changes in other locations, i was in a need to put Producer into Sth as well:
struct Sth { SomeClass(): name_("sth"), producer_(name_, factory_) // Oops - factory_ is not yet initialized! { factory_.add("type", builderFunction); } std::string name_; Factory factory_; Producer producer_; };
the problem was order – i needed to initialize factory_ first (this has been done in a c-tor body so far) and then initialize producer_. the obvious solution was to add a helper function, that would return properly-defined factory, and then call it from my c-tor, to initialize class' member:
Factory createFactory() { Factory f; f.add("type", builderFunction); return f; } struct Sth { SomeClass(): name_("sth"), factory_( createFactory() ), producer_(name_, factory_) // ok - factory_ is now properly initialized { } std::string name_; Factory factory_; Producer producer_; };
this is however a bit non-local. either i had to create function, in a unnamed namespace, inside my implementation, or detail namespace if this is all in header, or create private, static member function to do this work for me.
can this be done better? using C++14 (and C++11, with a bit more code) it can. the idea is to provide a lambda, that would do the initialization and call it in place:
struct Sth { SomeClass(): name_("sth"), factory_( [](){ Factory f; f.add("type", builderFunction); return f; }() ), // note the ending '()'! producer_(name_, factory_) // ok - factory_ is now properly initialized { } std::string name_; Factory factory_; Producer producer_; };
this way initialization is done in-place, using local code only. more over, there is a good change compiler will catch your drift and simply insert expected code inline, w/o a need to do any function-calling, objects moving, etc…
honestly this pattern is trivial. it is trivial to the point i was surprised i have not used it until now… ;)