[Container] Forward declaration of movable types
Hi, I am evaluating container library by Ion Gaztanaga. I know it is not officially a part of boost yet but I didn't know where else to ask this question. My problem: If there is a movable class 'MyMovable' (I use boost.move) it can be stored without unnecessary copies in move-aware vector that is declared like so: vector<MyMovable> movables; which is very cool until this vector is suppose be a member of other class like: //widget.hpp class Widget { vector<MyMovable> movables_; }; which is not cool at all because now widget.hpp must include my_movable.hpp as opposed to just forward declaring it if it stored say vector<shared_ptr<MyMovable>> which in turn makes movability of MyMovable rather useless. I guess my question is: how can one take advantage of move semantics in context of member containers without adding unnecessary class dependencies? Regards, Simon
El 16/08/2011 15:00, Szymon Gatner escribió:
My problem:
If there is a movable class 'MyMovable' (I use boost.move) it can be stored without unnecessary copies in move-aware vector that is declared like so:
vector<MyMovable> movables;
which is very cool until this vector is suppose be a member of other class like:
//widget.hpp
class Widget { vector<MyMovable> movables_; };
You always need the include, just like you needed it when MyMovable was copyable, move is an optimization it does not offer type erasure. If you need to break dependencies, use pimpl idiom for all your members (Warning: I haven't compiled it): //hpp //Note: no includes needed here for vector or MyMovable class Widget { Widget(); ~Widget(); private: //No explicit member declaration //shadow it behind Internals struct Internals; Internals *impl_; }; //cpp #include<vector> #include"MyMovable.h" struct Widget::Internals { vector<MyMovable> movables_; vector<MyMovable> movables2_; vector<MyMovable> movables3_; }; Widget::Widget() //Single allocation for all internal types : impl_(new Internals) {} Widget::~Widget() { delete impl_; } Best, Ion
2011/8/16 Ion Gaztañaga <igaztanaga@gmail.com>: [snip]
You always need the include, just like you needed it when MyMovable was copyable, move is an optimization it does not offer type erasure.
Yeah I get that that. I probably wasn't clear, I only meant that no include was needed if instances were kept by (smart) pointer and not by value.
If you need to break dependencies, use pimpl idiom for all your members (Warning: I haven't compiled it):
[snip] So there is no really a way around it, is there? Either: 1) objects in container have to be kept by pointer to break dependency but then they have to be allocated on the heap so there is little or no benefit in making them movable (as smart pointer will provide necessary copy/move/ownership semantics) 2) whole container has to be pimpled so that hidden implementation may then take advantage of all move goodies like container emplacing I feel like 2nd option is the one that gives real benefits. Is that right? Also, I often write code like: class Widget { public: typedef std::vector<shared_ptr<MyMovable> > MyMovables; const MyMovables& movables() const; private: MyMovables movables_; } so that clients can later do: BOOST_FOREACH(MyMovable& m, indirect(widget->movables()) { // do something with m } how to design Widget's class API in similar way if MyMovables is hidden behind pimpl? I mean: class Widget { public: ??? movables() const // what return type here? private: class Internals; Internals* pimpl_; } or maybe an API is just broken altogether? PS. I just compiled such a code: //widget.hpp class Movable; // only forward declared class Widget { boost::container::vector<Movable> movables_; } and widget.cpp properly adds Movable instances by moving them to movable_. Why does this work? If this is normal then I have no problem at all ;) Best regards and thanks for such a great libraries, Simon
El 16/08/2011 19:05, Szymon Gatner escribió:
So there is no really a way around it, is there? Either:
1) objects in container have to be kept by pointer to break dependency but then they have to be allocated on the heap so there is little or no benefit in making them movable (as smart pointer will provide necessary copy/move/ownership semantics)
2) whole container has to be pimpled so that hidden implementation may then take advantage of all move goodies like container emplacing
I feel like 2nd option is the one that gives real benefits. Is that right?
Right.
Also, I often write code like:
class Widget { public: typedef std::vector<shared_ptr<MyMovable> > MyMovables;
const MyMovables& movables() const;
private:
MyMovables movables_; }
so that clients can later do:
BOOST_FOREACH(MyMovable& m, indirect(widget->movables()) { // do something with m }
how to design Widget's class API in similar way if MyMovables is hidden behind pimpl?
//hpp class MyMovable; class Widget { public: typedef std::vector<MyMovable> MyMovables; const MyMovables& movables() const; private: class Internals; Internals *impl_; }; //cpp #include "MyMovable.hpp" const Widget::MyMovables& Widget::movables() const { return impl_->movables_; }
PS. I just compiled such a code:
//widget.hpp
class Movable; // only forward declared
class Widget { boost::container::vector<Movable> movables_; }
and widget.cpp properly adds Movable instances by moving them to movable_.
Why does this work? If this is normal then I have no problem at all ;)
Because you haven't instantiated the type. This does not compile: #include<boost/container/vector.hpp> class Movable; // only forward declared class Widget { boost::container::vector<Movable> movables_; }; int main() { Widget w; } However, I just checked that if you put all the functions in a cpp it will work fine. I think you've discovered a great Boost.Container feature derived from the care I put to support recursive types and avoid too early Movable instantiations: //hpp #include<boost/container/vector.hpp> class Movable; // only forward declared class Widget { boost::container::vector<Movable> movables_; public: Widget(const Widget &); Widget &operator= (const Widget &); Widget(); ~Widget(); }; //cpp //here implement constructor/destructor/assignment... The difference with pimpl is that you need to include "vector.hpp", with pimpl, you don't need to. Best, Ion
participants (2)
-
Ion Gaztañaga
-
Szymon Gatner