Le 8 oct. 2014 à 14:32, Joaquin M Lopez Munoz
Akim Demaille
writes: Le 7 oct. 2014 à 20:41, Joaquin M Lopez Munoz
a écrit : A slightly smarter approach involves a cloning class that accepts a const Base& and does only clone on copy, thus avoiding dynamic memory allocation when the element already exists.
That's nice!
Ah, you should add a move ctor and assignment operator for better performance. Revised example:
Great :) Finally, the free-lunch is not over, I should just let time pass and have my implementation improve all by itself :)
Do you have any gut feeling about whether there should be only a single Exp-level hash-consing, or something with one flyweight per AST type (and conversions). I'm still considering both.
Do you mean having
using ExpBin=poly_flyweight<Bin>; using ExpNum=poly_flyweight<Num>;
rather than a single using ExpBin=poly_flyweight<Bin>? I think this is very hard to manage: to begin with, Bin has two Exp members, with this type splitting it is not even clear how you would manage the different cases where the operands to Bin are compound expressions or Num's (and the combinations thereof).
I expect the flyweight implementation to support inheritance, just as std::shared_ptr<base> p = std::make_shared<derived>(); works. Which also means that I need variations around std::dynamic_pointer_cast etc., but it might be worth it. I have experimentations on this on my real project, but it does not work. I'll try to do that in the case of the simple toy hierarchy you and I used so far.
You lost me here, I have no idea what special trick in std::optional would prevent Flyweight from completing its forwarding to such operators.
It is a basic difference in indirection handling. Let me explain: std::optional<T> defines operator-> as
optional<T> --> const T*
which, for the case of optional
(ptr being some pointer-like type), yields optional
--> const ptr<T>* For instance:
optional
--> const T** With the interface you propose for flyweight, we'd have operator-> for flyweight
defined as optional
--> const T* So, there is a choice between having -> behave with "pointer semantics" (std::optional) or "pass-thru" semantics, for want of a better name.
Ouhh, I clearly prefer the pass-thru semantics here. The Flyweight is similar (to my eyes) to a proxy to an object that does all it can to have the rest of the code believe it is the real object. I'm very happy that I can enable/disable flyweight'ing via a simple typedef, and using a pointer-like semantics would break everything: the real object and the flyweight'ed one would have different interfaces.
If we choose pass-thru semantics, then we're ruling out the possibility of having operator-> in instantiations such as, say, flyweight<int>, where the flyweight'd element is not a (smart) pointer.
I'm not sure I understand what you mean here. I can't imagine what 'flyweight<int>(42)->...' would mean. I'd sfinae it out of the picture.
If I were to decided, I'd go for pointer semantics, which is more generally applicable. On the other hand, you can have your own poly_flyweight<T> class like the one shown with pointer semantics (because the intermediate pointer indirection is hidden in the implementation). Not sure I made myself clear :-)
Actually it's precisely because it forwards the operator-> call to the wrapped that I see this as pass-thru. The operator-> and operator* behaves as if I were talking to the shared_ptr, not to the flyweight. But maybe I misunderstood what you meant here.