[move] moving temporaries of copyable types

Hi, I've recently taken a look at the Boost.Move library and it seems that it is only possible to make 1) either non-copyable types movable or 2) make copyable types move-aware by explicitly moving them. Now, I've discovered for myself that it is possible for pre-c++09 compilers to make copyable types *implicitly* movable by detecting temporaries that can be moved. So, my question is whether this is well-known and this feature was deliberately excluded from Boost.Move or nobody has discovered it yet. It would be great to have support for copy- and (implicit) move-semantics. Klaus Triendl

klaus triendl wrote:
So, my question is whether this is well-known and this feature was deliberately excluded from Boost.Move or nobody has discovered it yet. It would be great to have support for copy- and (implicit) move-semantics.
I didn't know about this feature. Could you elaborate a bit? And it would be even better if you could modify the library to support this feature ;-) Best, Ion

Ion Gaztañaga schrieb:
klaus triendl wrote:
So, my question is whether this is well-known and this feature was deliberately excluded from Boost.Move or nobody has discovered it yet. It would be great to have support for copy- and (implicit) move-semantics.
I didn't know about this feature. Could you elaborate a bit? And it would be even better if you could modify the library to support this feature ;-)
Well, it's actually very simple: add not only a user-defined conversion to rv<T>& but to const rv<T>& as well and have a ctor/assignment operator handling the const rv<T>&. Compilers will choose then a conversion operator based on the const-qualification of an object. This technique is working on msvc9. I let a short code example speak: <code> template<typename T> class rv: public T {}; class copyable_and_movable { typedef copyable_and_movable type; public: // non-const lvalues bind here copyable_and_movable(copyable_and_movable& other) {} // const lvalues and const rvalues bind here copyable_and_movable(const rv<type>& other) {} // non-const rvalues bind here - temporaries! copyable_and_movable(rv<type>& other) {} // ... assignment operators ditto operator rv<type>&() { return static_cast<rv<type>& >(*this); } operator const rv<type>&() const { return static_cast<const rv<type>& >(*this); } }; </code> I'm not quite sure yet how Boost.Move could support this - the caveat lies in having essentially two copy constructors. A possible idea is: Extend the BOOST_ENABLE_MOVE_EMULATION macro to include the const conversion operator. Provide a macro for the const-copy ctor: #if !defined(BOOST_HAS_RVALUE_REFS) # define BOOST_CONST_RV_REF(TYPE) const boost::rv< TYPE >& #else # define BOOST_CONST_RV_REF(TYPE) const TYPE& #endif When writing copyable_and_movable guard the non-const-copy ctor with the BOOST_HAS_RVALUE_REFS macro. In code: <code> class copyable_and_movable { typedef copyable_and_movable type; public: #if !defined(BOOST_HAS_RVALUE_REFS) // non-const lvalues bind here copyable_and_movable(copyable_and_movable& other) {} #endif // const lvalues and const rvalues bind here copyable_and_movable(BOOST_CONST_RV_REF(type) other) {} // non-const rvalues bind here - temporaries! copyable_and_movable(BOOST_RV_REF(type) other) {} // ... assignment operators ditto BOOST_ENABLE_MOVE_EMULATION(type) }; </code> Attached you find a sample project just showing my tests without using Boost.Move. Best, Klaus

klaus triendl escribió:
Ion Gaztañaga schrieb:
klaus triendl wrote:
So, my question is whether this is well-known and this feature was deliberately excluded from Boost.Move or nobody has discovered it yet. It would be great to have support for copy- and (implicit) move-semantics.
Hi, Sorry for the looong delay, but I've just fond time to investigate it these days. In short, your approach is viable at least using MSVC 8&9, Intel 9.0 (so I suppose it will work on any EDG frontend compiler) and gcc 4.3 (I haven't tested other gcc compilers). Sadly, MSVC-7.1 has some problems when forwarding types with const rv<T> & in copy constructors and overall the need of an ifndef is quite ugly but I can't find any workaround (if only we had constructing forwarding...) Trying to write something portable, copyable and movable types can be written like this: class copymovable { public: BOOST_ENABLE_MOVE_EMULATION(copymovable) //Copy constructor and assignment. //BOOST_COPY_REF is expanded to 'const rv<T> &' in c++98, //and to 'const T &' in C++0x copymovable(BOOST_COPY_REF(copymovable) o){} copymovable & operator=(BOOST_COPY_REF(copymovable) o) {} //Move constructor and assignment. //BOOST_RV_REF is expanded to 'rv<T> &' in c++98, //and to 'T &&' in C++0x copymovable(BOOST_RV_REF(copymovable) o) copymovable & operator=(BOOST_RV_REF(copymovable) o) {} //Additional copy constructor and assignment for c++98 //compilers #ifndef BOOST_HAS_RVALUE_REFS copymovable(copymovable &o) copymovable & operator= (copymovable & o) {} #endif }; The additional assignment could be produced with a macro forwarding to the other constructor but the additional copy constructor can't generated and that's a pity. As you discovered, I've checked that this approach allows moving temporaries of copyable values: copymovable produce(); copymovable cm; //... //Moved, not copied cm = produce(); To sump up, this approach allows moving from non-const rvalues but discards Visual 7.1 (I don't know if this should be still supported, though) and syntax is much uglier. I think this should be discussed in the mailing list before any proposal is presented for review. If container writers want to support also catching temporary copymovable types they should write also 3 versions (or catch it by value): void push_back(BOOST_COPY_REF(T) t); void push_back(BOOST_RV_REF(T) t); #ifdef BOOST_HAS_RVALUE_REFS void push_back(T &t); #endif Thanks a lot for your help and patience! Ion

klaus triendl wrote:
Hi,
I've recently taken a look at the Boost.Move library and it seems that it is only possible to make 1) either non-copyable types movable or 2) make copyable types move-aware by explicitly moving them.
Now, I've discovered for myself that it is possible for pre-c++09 compilers to make copyable types *implicitly* movable by detecting temporaries that can be moved.
So, my question is whether this is well-known and this feature was deliberately excluded from Boost.Move or nobody has discovered it yet. It would be great to have support for copy- and (implicit) move-semantics.
Sorry for not catching this yet because I can't find time to fully understand your approach and do some portability tests. Just to make things a bit easier when I have some time, what do you mean with "make copyable types *implicitly* movable"? Can you give me a concrete example of what's possible with your approach that was not possible before? Best, Ion

Ion Gaztañaga schrieb:
klaus triendl wrote:
Hi,
I've recently taken a look at the Boost.Move library and it seems that it is only possible to make 1) either non-copyable types movable or 2) make copyable types move-aware by explicitly moving them.
Now, I've discovered for myself that it is possible for pre-c++09 compilers to make copyable types *implicitly* movable by detecting temporaries that can be moved.
So, my question is whether this is well-known and this feature was deliberately excluded from Boost.Move or nobody has discovered it yet. It would be great to have support for copy- and (implicit) move-semantics.
Sorry for not catching this yet because I can't find time to fully understand your approach and do some portability tests. Just to make things a bit easier when I have some time, what do you mean with "make copyable types *implicitly* movable"? Can you give me a concrete example of what's possible with your approach that was not possible before?
Ok, I'm working on my ability to explain :) Simply put: <code> clone_ptr<int> return_rvalue() { return clone_ptr<int>(new int(1)); } clone_ptr<int> p; // Boost.Move ---> temporary gets copied // my approach ---> temporary gets moved p = return_rvalue(); </code> Best, Klaus

on Wed Jun 24 2009, klaus triendl <klaus-AT-triendl.eu> wrote:
Sorry for not catching this yet because I can't find time to fully understand your approach and do some portability tests. Just to make things a bit easier when I have some time, what do you mean with "make copyable types *implicitly* movable"? Can you give me a concrete example of what's possible with your approach that was not possible before?
Ok, I'm working on my ability to explain :)
Simply put: <code> clone_ptr<int> return_rvalue() { return clone_ptr<int>(new int(1)); }
clone_ptr<int> p; // Boost.Move ---> temporary gets copied // my approach ---> temporary gets moved p = return_rvalue(); </code>
In both cases the temporary should get RVO'd (i.e. the copy should be elided) on all modern compilers. If you can find a modern compiler where Boost.Move does not RVO, then its design needs to be fixed. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

David Abrahams escribió:
Simply put: <code> clone_ptr<int> return_rvalue() { return clone_ptr<int>(new int(1)); }
clone_ptr<int> p; // Boost.Move ---> temporary gets copied // my approach ---> temporary gets moved p = return_rvalue(); </code>
In both cases the temporary should get RVO'd (i.e. the copy should be elided) on all modern compilers. If you can find a modern compiler where Boost.Move does not RVO, then its design needs to be fixed.
Yes in case of constructors but not with assigments: clone_ptr<MyClass> p; for(){ p = return_rvalue(); } Although I guess that this missed optimization might not be so important. The same could be applied to containers //With old approach, copied instead vector.push_back(return_rvalue()); Best, Ion

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 10 August 2009, Ion Gaztañaga wrote:
David Abrahams escribió:
In both cases the temporary should get RVO'd (i.e. the copy should be elided) on all modern compilers. If you can find a modern compiler where Boost.Move does not RVO, then its design needs to be fixed.
Does Boost.Move support moveable-but-not-copyable types? Could this cause compile errors for such types when compiling with optimization turned off?
Yes in case of constructors but not with assigments:
clone_ptr<MyClass> p;
for(){ p = return_rvalue(); }
Although I guess that this missed optimization might not be so important. The same could be applied to containers
//With old approach, copied instead vector.push_back(return_rvalue());
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkqAN58ACgkQ5vihyNWuA4Vv+ACgzMVs/IUV9dkC5UZYfkGF5Fa9 AwkAoKxCExgH7lvJULK/iRr6TfArQ3Cs =xP5S -----END PGP SIGNATURE-----
participants (5)
-
Christian Schladetsch
-
David Abrahams
-
Frank Mori Hess
-
Ion Gaztañaga
-
klaus triendl