Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
If you want a best practice, I'm pretty sure the best practice is to do the straightforward and "naive" thing. From the perspective of OOP, your complaints are misguided. The unit of coherence isn'...
Answer
#1: Initial revision
If you want a best practice, I'm pretty sure the best practice is to do the straightforward and "naive" thing. From the perspective of OOP, your complaints are misguided. The unit of coherence isn't a function but a class. What's happening in the constructor should not be "unrelated code". Also, in the scenario you describe, there's exactly one method that would use the pre-initialized variable, and it's within the same class. A "Find All References" or even a basic string search should quickly find it. You don't need to painstakingly audit every line of the class' code, let alone any other code. Ideally, the class isn't so large that even reviewing everything would be very onerous, but, as just mentioned, that should be unnecessary. After that, the next option would be to just have the function initialize the value on first execution. For example, you have an empty `std::optional` member that the function populates at the beginning if it is empty. You could make a little wrapper around this, similar to Rust's [OnceCell](https://doc.rust-lang.org/std/cell/struct.OnceCell.html), to improve safety and better communicate the intended usage. This approach would be beneficial if there is a decent likelihood that the expensive calculation may *never* need to be computed, *and* if performing the expensive calculation when the method is called is acceptable. This approach also does colocate the initialization code with the code that uses it, but you pay for the check every method invocation and there is a risk that some code will use the member before it's initialized. Admittedly, the latter should not be a significant concern in this use-case, and if you make a safe wrapper should be quickly caught. Other approaches are unlikely to interact well with inheritance or seem idiomatic. Nevertheless, I will rationalize the approach you describe. The way this would typically be handled in a functional language is that you'd return a closure that closes over the precomputed value. For example, the following two functions in Haskell ```haskell myFunc1 = \x -> let expensive = ... in ... expensive ... myFunc2 = let expensive = ... in \x -> ... expensive ... ``` have exactly the same type, but `myFunc1` will compute `expensive` every time it's called, while `myFunc2` is bound to a lambda that closes over `expensive` which will only be computed once. You can do the same thing in C++. Unfortunately, for the scenario you describe, you'd typically want to *move* `expensive` into the lambda, and not copy or reference it, so that it gets destroyed when the lambda does and isn't copied. This can be done in a reasonable way in C++14, but C++11 needs work-arounds. Of course, you could *manually* create the closure type which is essentially what your `PrecalculatingInitializer` would be. (Modulo the "registering" stuff you talk about. You can just have values of such types be members of the class. There's no need to "register" these in some "common place". Maybe this is what you meant by "registering".) This approach will have some mildly awkward interactions with inheritance. You can implicitly (with lambdas) or explicitly pass in `this` to get access to member functions. If you use an explicit closure type, it will likely need to be declared a friend class, unless you nest it which is a reasonable thing to do in this case. A bigger issue is that you can't really override these closures. You could make a wrapper method that just delegates to the closure, and that wrapper method could be overridden. This would lead to wasted space storing the closure, but that is arguably about as bad as what would happen in the analogous situation for the other approaches. In the very likely case that you don't actually care about inheritance here, the main issue with this last approach is that it is relatively verbose and not the most idiomatic code. It's quite verbose if you need to manually make closure types. If you use lambdas (modulo the C++11 limitations) it's not too bad, but you'd likely still want to have an "init" function for each closure. This wouldn't be much more verbose than the "naive" approach, but it would be a bit unusual looking and likely have some minor overheads.