The move semantic is a concept introduced in the C++0X standard. If a temporary is assigned to a variable, it gives the programmer the opportinity to "move" its content instead of copying it. These temporaries are also known as right value ( shortened to RValue ). To differentiate the copy constructor from the move constructor, The RValue reference T&& has been introduced. Unfortunately, there's small chances this feature will be available anytime soon for all compilers we use. Mojito, our internal engine, is currently running on 11 platforms (Pc, Mac, iOs, linux, android, bada, PSP, Ps3, Xbox360 and finally Wii ). This post will introduce a technique to simulate this behavior. Even if it need some hand written code, an entire code base can benefit from such a feature.
Let's introduce the move constructor in C++0X : DummyClass( DummyClass && other ); To emulate this feature, move constructor needs to be different from the copy constructor. A well-known paradigm states that "Every problem in computer science can be solved by adding a level of indirection". And it again proves true in this case. Let's define a template class that only hold a reference to the data. template<typename _T_>class Move
{
public:
explicit Move( _T_ & value )
:Value( value )
{
}
_T_ & Get() const
{
return Value;
}
private:
_T_ & Value;
}; The move constructor can then be written : DummyClass( const Move<DummyClass> & other ); And the same concept can be applied to the assignment operator : DummyClass& operator=( const Move<DummyClass> & other ); Let's introduce a simple example, the swap. A classic swap function could be written like this. template< typename _T_>
inline void Swap( _T_ & first, _T_ & second )
{
_T_ temp;
temp = first;
first = second;
second = temp;
} If _T_ is a collection, this swap incurs 3 table copy-constructors that allocates memory and copy-construct items. Now with our new technique, we can define a swap that does not allocate memory if _T_ supports our move operator. template< typename _T_>
inline void FastSwap( _T_ & first, _T_ & second )
{
_T_ temp;
temp = Move<_T_>( first );
first = Move<_T_>( second );
second = Move<_T_>( temp );
} Pointers are moved around from table to table, but no allocation nor copy occurs. The problems is now every class should implement the move constructor and/or operator. Moreover, it's not possible to implement them for primitive types. The solution is to render the Move transparent if it is not supported. A simple C++ rules can be recalled : 2 conversions are allowed when calling a function, either 2 language conversions or 1 language + 1 user conversion. Let's take advantage of this rule. template<typename _T_>
class Move
{
public:
….
operator _T_ & ()
{
return Value;
}
….
}; Now, we can analyze what's happening in the following statement. temp = Move<_T_>( first ); A RValue of type Move<_T_> is created with first as constructor argument.
if an operator= that takes a Move<_T_> exists, it is called.
if not, there still exist a valid conversion, the operator= is called with _T_&
That's it. This code now works for all types, even primitive ones. This system can be extended to return value. A Moveable<_T_> can be returned instead of _T_, selecting the move operator when available. This is left as an exercise for the reader. A small demo is available here.