This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Implementing clean and simple move symantics
- From: Chris Jefferson <caj at cs dot york dot ac dot uk>
- To: libstdc++ at gcc dot gnu dot org
- Date: Tue, 23 Nov 2004 17:55:36 +0000
- Subject: Implementing clean and simple move symantics
Hopefully the title of this e-mail won't be too strong!
After looking at various methods of implementing move symantics, the way
I personally like the best looks like:
//Set this to 1 for types you want to be moveable
template<typename _Tp>
struct _Moveable
{ enum {_M_type = 0}; };
//Simple wrapper class
template<typename _Tp>
struct _MoveWrapper
{
_Tp& __wrapped;
_MoveWrapper(_Tp& __wrapped_in) : __wrapped(__wrapped_in) { }
};
//Helper for __move function for when type is not moveable
template<typename _Tp, int _Int = _Moveable<_Tp>::_M_type>
struct _MoveReturnType
{ typedef _Tp& _Type; };
//Helper for _move function for when type is moveable
template<typename _Tp>
struct _MoveReturnType<_Tp, 1>
{ typedef _MoveWrapper<_Tp> _Type; };
// Denotes an object can be moved.
template<typename _Tp>
typename _MoveReturnType<_Tp>::_Type __move(_Tp& __moved)
{ return __moved; }
For most types T , using this involves 2 steps:
1) Add an overload to _Moveable while sets _M_type to 1.
2) Add a new constructor which takes _Moveable<T> t, performs default
construction then does this->swap(t)
3) Add a new operator= which takes _Moveable<T> t and performs
this->swap(t);
Changing the existing code consists of two main parts:
1) (obvious) adding __move around objects which can be moved
2) lots of function call things like copy_backward, or
copy_and_allocate. Introduce new functions called move_backward and
move_and_allocate which do the same thing, but using __move where possible.
Note that this does not deal with the issue of temporaries,
particularily those generated when calling functions. Dealing with such
things a) generally and b) without damaging the speed of simple types
(like int, char, etc) appears like it would be very difficult. There are
quite a few places in the standard library where such temporaries can
actually be removed or reduced in number by being careful.
For example, std::sort introduces temporaries in 2 places. Firstly when
partioning it creates a temporary which is the result of
median(begin,end,begin+(end-begin)/2), and when calling
__unguarded_insert. Both of these can be avoided without damaging
complexity with careful coding (in median's case, copy the value into a
temporary before calling the function, then pass by reference. In
__unguarded_insert's case, inline the function as it is only used once).
I have already tried adding move symantics to vector and list and a few
algorithms. It seems as if it will work quite well and the improvements
in performance are (unsuprisingly) quite impressive. However this would
also be quite a large scale change to almost every file, and so I'd
perfer a few people say they are happy with the idea, and exactly how
would be the best way to progress before beginning any serious coding.
Chris