On Fri, Jun 21, 2019 at 06:32:33PM +0000, Yatheendra via Digitalmars-d-learn wrote: [...] > struct CostlyComputeResult { > ... // data fields > // constructor takes compute results, no postblit > } > > struct Wrapper { > const (CostlyComputeResult) *cachee = 0; > ... // data fields storing compute inputs > // constructor takes compute inputs > // pointer to function(compute inputs) > const ref get() { > if (!cachee) { > cachee = new(function(inputs)); > } > return cachee; > } > } > > Hopefully jumping through these hoops is worth the while. Instead, > maybe just wait until the compiler grows a 'cache pure' function > qualifier (move constructor required?).
The problem with this is that you cannot use const(Wrapper). In particular, if you have a function that wants to document that it does not mutate its argument, you cannot write: auto func(in Wrapper data) { ... } because const(Wrapper) does not allow lazy initialization. Basically you have to resort to convention (e.g., name it ReadOnly or something similar) rather than actually mark it const. This generally isn't a big problem if you're using func in isolation, but as soon as you need to compose func with other const code, you quickly find yourself in a gordian knot of const incompatibilities that percolate throughout the entire call chain, because D's const is transitive. I.e., you want func to interact with other functions that trade in const data, but you cannot because of the constant(!) need to keep Wrapper mutable. The non-constness of Wrapper will then percolate up the call chain, "tainting" all functions that call it so that they cannot be marked const, even though *logically* they are const. This mix is already bad enough (try it on a non-trivial codebase sometime and see for yourself), but once you add generic functions to the mix, the whole thing simply becomes unusable -- because generic functions expect to write const types as const(T), but that will break if T is Wrapper. OK, so you can try to make it mutable as a workaround. But then that breaks const-ness attribute inference so the generic function becomes non-const, which in turn recursively causes its callers to be non-const, etc. Somewhere at the top of the call chain you'll have a const method that wants to call a const function, passing some higher-level data structure that eventually contains Wrapper somewhere deep down -- and it simply doesn't work without making the *entire* structure mutable, due to the infectiousness of const. So as soon as you use Wrapper in any data structure of arbitrary complexity, the entire thing must be mutable -- otherwise const percolates all the way down to Wrapper and the caching doesn't work anymore. tl;dr: using a wrapper works fine for relatively simple cases. But as soon as you add any meaningful complexity to it, the scheme quickly becomes either impractically convoluted, or outright impossible to use without a hard cast to cast away const (thereby invoking the lovely UB). T -- INTEL = Only half of "intelligence".