|
Boost Users : |
Subject: Re: [Boost-users] Flyweight: wrapping shared_ptr
From: Joaquin M Lopez Munoz (joaquin_at_[hidden])
Date: 2014-10-07 14:41:15
> De: Boost-users [mailto:boost-users-bounces_at_[hidden]]
> En nombre de Akim Demaille
> Enviado el: martes, 07 de octubre de 2014 18:27
> Para: Boost Users List
> Asunto: Re: [Boost-users] Flyweight: wrapping shared_ptr
>
> Le 7 oct. 2014 à 18:18, JoaquÃn Mª López Muñoz <joaquin_at_[hidden]> a écrit :
>
> > An alternative would be to use some sort of cloning smart pointer to
> > spare the reference count, but it's not clear to me this would be
> > faster than
> > shared_ptr:
> >
> > * element exists: shared_ptr creation is slower than clone_ptr
> > creation
> > * element does not exist: shared_ptr creation and copy is probably
> > faster than clone_ptr creation and cloning.
>
> Yes, indeed.
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.
This can be nicely wrapped up as follows:
template<typename Base>
class poly_holder
{
public:
poly_holder(const Base& x):p(&x),dlt(false){}
poly_holder(const poly_holder& x):p(x.p->clone()),dlt(true){}
poly_holder& operator=(const poly_holder& x)
{
Base* q=x.p->clone();
if(dlt)delete p;
p=q;
dlt=true;
return *this;
}
~poly_holder(){if(dlt)delete p;}
operator const Base&()const {return *p;}
private:
const Base* p;
bool dlt;
};
template<typename Base>
class poly_flyweight:public boost::flyweight<poly_holder<Base>>
{
public:
using super=boost::flyweight<poly_holder<Base>>;
using super::super;
const Base& operator*() const{return base();}
const Base* operator->()const{return &base();}
private:
const Base& base()const{return this->get();}
};
template<typename Base>
std::size_t hash_value(const poly_flyweight<Base>& x)
{
return boost::hash<const Base*>()(&*x);
}
template<typename Base>
bool operator==(
const poly_flyweight<Base>& l,const poly_flyweight<Base>& r)
{
return
static_cast<boost::flyweight<poly_holder<Base>>>(l)==
static_cast<boost::flyweight<poly_holder<Base>>>(r);
}
template<typename Base>
bool operator!=(
const poly_flyweight<Base>& l,const poly_flyweight<Base>& r)
{
return !(l==r);
}
I've rewritten your test case to take advantage of this poly_flyweight
thing, cleaning up some unnecessary hash and operator== overloads along the
way and without resorting to the intermediate Exp_ type; see:
http://coliru.stacked-crooked.com/a/76e87531b65d44f9
> Is there anyway Flyweight could have supported the operator-> natively?
> It seems that with some SFINAE under the hood, it would work, don't you think?
Yep, that would be possible, the only reason I didn't do it is to not
clutter the interface.
Also, some std classes such as (C++17) std::optional implement operator*
differently, without the extra dereference required by smart pointer semantics.
JoaquÃn M López Muñoz
Telefónica
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net