Boost logo

Boost :

Subject: [boost] Composing non copyable and movable classes
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2008-12-06 13:00:42


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 addition X can have also member that are 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_;
    // ...
};

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).

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?

Vicente


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