
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