Akim Demaille <akim <at> lrde.epita.fr> writes:
Le 8 oct. 2014 à 14:32, Joaquin M Lopez Munoz <joaquin <at> tid.es> a écrit :
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 :)
Anyway, please profile against shared_ptr, I'm not claiming this is necessarily faster.
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>();
This is alas not the case for flyweight<base>/flyweight<derived>. You can simulate something like that behavior in poly_flyweight: template<typename Derived> class poly_flyweight { ... operator flyweight<Base>()const; ... }; but invoking this operator for a poly_flyweight<Derived> object x would imply creating a clone of x.get() (upcasted to const Base*) in poly_flyweight<Base> internal factory, which is a waste of space and brings you to the original situation where all objects are stored in the same factory. A less ambitious approach is template<typename Derived,typename Base> class derived_poly_flyweight:poly_flyweight<Base> { ... const Derived& operator()const; // similar for operator* and operator-> ... }; which gives you direct conversion to const Derived&, but still stores everything in the same factory.
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.
I understand your rationale, but pass-thru semantics is only applicable to pointer-like elements.
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.
int is a poorly chosen example. Take instead struct foo { int x; }; With pointer-like semantics, you have flyweight<foo> f; flyweight<foo*> g; f->x=0; // OK g->x=0; // error with pass-thru semantics, it is the other way around: f->x=0; // error g->x=0; // OK The case with poly_flyweight is different. As poly_flyweight<Base> is expected to act as a replacement for const Base*, pass-thru semantics makes a lot of sense. Joaquín M López Muñoz Telefónica