Boost logo

Boost :

Subject: Re: [boost] Composing non copyable and movable classes
From: David Abrahams (dave_at_[hidden])
Date: 2008-12-06 19:42:19


on Sat Dec 06 2008, "vicente.botet" <vicente.botet-AT-wanadoo.fr> wrote:

> Hi,
>
> Imagine a class X which contains several noncopyable and movable members Ci mi;.
>
> class X : noncopyable {
> C1 m1;
> C2 m2;
> // ...
> Ck mk;
> // ...
> };
>
> Very often a class is made movable by storing a pointer to some
> implementation data on a shared_ptr. Without allocator this means a
> heap new/delete. If the class accept allocators this means an
> allocate/deallocate on the allocator.
>
> class Mi : noncopyable {
> struct implementation_data;
> shared_ptr<implementation_data> data_;
> // ...
> };
>
> The question is how to make X movable efficiently. The first way is to
> move each one of the movable objects one by one. This could be
> expensive if the number of members is high.

In general it isn't high enough to justify doing anything else.

> In addition X can have also member that are not movable.

Do you mean "copiable but not otherwise movable,' or simply not movable?

> The second approach is to use the same technique, store a pointer to
> the data on a shared_pointer, and move on one operation all the
> members via the pointer.
>
> class X : noncopyable {
> struct implementation_data {
> C1 m1;
> C1 m2;
> // ...
> C1 mk;
> };
> shared_ptr<implementation_data> data_;
> public:
> // ...
> };
>
> In this case the class X don't need any more that its members be
> movable. In order to avoid the allocation/deallocation of the
> respective classes Ci, it would be interesting to have a class which
> is not copyable and not movable and that owns the implementation
>
> class nonmovable_Ci : noncopyable {
> struct implementation_data;
> implementation_data data_;
> // ...
> };

To justify that, moving one of the C's must be really expensive, or
you have to expect many, many moves of each object to balance the cost
of allocation. In general, I'd consider using the small object
optimization to handle cases like this.

> Except the movable support, the nonmovable_Ci class share with Ci the
> same functionality. But is not related to Ci by inheritance. Both
> classes model a common concept. Of course this can not be applied if
> the implementation_data must be hidden in some .cpp file (pimpl
> idiom).

This feels like overgeneralizing. Unless otherwise justified, every
type should have value semantics, i.e., should be movable and copiable.

> The consequence is the classes and functions that should work with both
> implementations need to be templated.
>
> This allows the class X to allocate all the data at once.
>
> class X : noncopyable {
> struct implementation_data {
> nonmovable_C1 m1;
> nonmovable_C1 m2;
> // ...
> nonmovable_C1 mk;
> };
> shared_ptr<implementation_data> data_;
> public:
> // ...
> };
>
> I think that this separation is in line with the C++ principle "you
> don't pay for what you don't use". Any thoughts?

I think it makes me pay for a lot of complexity and for the cost of
managing dynamically-allocated resources. But that's just an initial
reaction. I'm not really sure I understand the context for what you're
proposing. Are you saying all classes should be designed this way,
or... ?

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk