Implicit Move when returning a local object

| Home | Libraries | People | FAQ | More

## Implicit Move when returning a local object

The C++ standard specifies situations where an implicit move operation is safe and the compiler can use it in cases were the (Named) Return Value Optimization) can't be used. The typical use case is when a function returns a named (non-temporary) object by value and the following code will perfectly compile in C++11:

    //Even if movable can't be copied
    //the compiler will call the move-constructor
    //to generate the return value
    //
    //The compiler can also optimize out the move
    //and directly construct the object 'm'
    movable factory()
    {
       movable tmp;
       m = ...
       //(1) moved instead of copied
       return tmp;
    };

    //Initialize object
    movable m(factory());

In compilers without rvalue references and some non-conforming compilers (such as Visual C++ 2010/2012) the line marked with `(1)` would trigger a compilation error because `movable` can't be copied. Using a explicit `::boost::move(tmp)` would workaround this limitation but it would code suboptimal in C++11 compilers (as the compile could not use (N)RVO to optimize-away the copy/move).

Boost.Move offers an additional macro called `BOOST_MOVE_RET` that can be used to alleviate this problem obtaining portable move-on-return semantics. Let's use the previously presented movable-only `movable` class with classes `copyable` (copy-only type), `copy_movable` (can be copied and moved) and `non_copy_movable` (non-copyable and non-movable):

    //header file "copymovable.hpp"
    #include <boost/move/core.hpp>

    //A copy_movable class
    class copy_movable
    {
       BOOST_COPYABLE_AND_MOVABLE(copy_movable)
       int value_;

       public:
       copy_movable() : value_(1){}

       //Move constructor and assignment
       copy_movable(BOOST_RV_REF(copy_movable) m)
       {  value_ = m.value_;   m.value_ = 0;  }

       copy_movable(const copy_movable &m)
       {  value_ = m.value_;   }

       copy_movable & operator=(BOOST_RV_REF(copy_movable) m)
       {  value_ = m.value_;   m.value_ = 0;  return *this;  }

       copy_movable & operator=(BOOST_COPY_ASSIGN_REF(copy_movable) m)
       {  value_ = m.value_;   return *this;  }

       bool moved() const //Observer
       {  return value_ == 0; }
    };

    //A copyable-only class
    class copyable
    {};

    //A copyable-only class
    class non_copy_movable
    {
       public:
       non_copy_movable(){}
       private:
       non_copy_movable(const non_copy_movable&);
       non_copy_movable& operator=(const non_copy_movable&);
    };

and build a generic factory function that returns a newly constructed value or a reference to an already constructed object.

    #include "movable.hpp"
    #include "copymovable.hpp"
    #include <boost/move/core.hpp>

    template<class Type>
    struct factory_functor
    {
       typedef Type return_type;

       Type operator()() const
       {  Type t;  return BOOST_MOVE_RET(Type, t);  }
    };

    struct return_reference
    {
       typedef non_copy_movable &return_type;

       non_copy_movable &operator()() const
       {  return ncm; }

       static non_copy_movable ncm;
    };

    non_copy_movable return_reference::ncm;

    //A wrapper that locks a mutex while the
    //factory creates a new value.
    //It must generically move the return value
    //if possible both in C++03 and C++11
    template <class Factory>
    typename Factory::return_type lock_wrapper(Factory f)
    {
       typedef typename Factory::return_type return_type;
       //LOCK();
       return_type r = f();
       //UNLOCK();

       //In C++03: boost::move() if R is not a reference and
       //has move emulation enabled. In C++11: just return r.
       return BOOST_MOVE_RET(return_type, r);
    }

    int main()
    {
       movable m            = lock_wrapper(factory_functor<movable>     ());
       copy_movable cm      = lock_wrapper(factory_functor<copy_movable>());
       copyable c           = lock_wrapper(factory_functor<copyable>    ());
       non_copy_movable &mr = lock_wrapper(return_reference             ());
       return 0;
    }

Caution: When using this macro in a non-conforming or C++03 compilers, a move will be performed even if the C++11 standard does not allow it (e.g. returning a static variable). The user is responsible for using this macro only used to return local objects that met C++11 criteria. E.g.:

    struct foo
    {
       copy_movable operator()() const
       {
          //ERROR! The Standard does not allow implicit move returns when the object to be returned
          //does not met the criteria for elision of a copy operation (such as returning a static member data)
          //In C++03 compilers this will MOVE resources from cm
          //In C++11 compilers this will COPY resources from cm
          //so DON'T use use BOOST_MOVE_RET without care.
          return BOOST_MOVE_RET(copy_movable, cm);
       }

       static copy_movable cm;
    };

Note: When returning a temporary object `BOOST_MOVE_REF` is not needed as copy ellision rules will work on both C++03 and C++11 compilers.

    //Note: no BOOST_MOVE_RET
    movable get_movable()
    {  return movable();  }

    copy_movable get_copy_movable()
    {  return copy_movable();  }

    copyable get_copyable()
    {  return copyable();  }