Стандарт C++ определяет ситуации, когда операция неявного перемещения безопасна, и компилятор может использовать ее в случаях, когда оптимизация возвратного значения (Named) не может быть использована. Типичный случай использования — это когда функция возвращает названный (не временный) объект по значению, и следующий код будет идеально компилироваться в C++11:
movable factory()
{
movable tmp;
m = ...
return tmp;
};
movable m(factory());
В компиляторах без ссылок на rvalue и некоторых несоответствующих компиляторах (например, Visual C++ 2010/2012) строка, отмеченная (1)
, вызовет ошибку компиляции, поскольку подвижный
не может быть скопирован. Использование явного ::boost::move(tmp)
обойдет это ограничение, но в компиляторах C++11 оно будет кодироваться неоптимально (поскольку компиляция не может использовать (N)RVO для оптимизации копии/перемещения).
Boost.Move предлагает дополнительный макрос под названием BOOST_MOVE_RET
, который можно использовать для облегчения этой проблемы, получая портативную семантику перемещения по возврату. Давайте воспользуемся представленным ранее классом movable
с классами copyable
(тип только для копирования), copy_movable
(можно копировать и перемещать) и non_copy_movable
(некопируемый и недвижимый):
#include <boost/move/core.hpp>
class copy_movable
{
BOOST_COPYABLE_AND_MOVABLE(copy_movable)
int value_;
public:
copy_movable() : value_(1){}
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
{ return value_ == 0; }
};
class copyable
{};
class non_copy_movable
{
public:
non_copy_movable(){}
private:
non_copy_movable(const non_copy_movable&);
non_copy_movable& operator=(const non_copy_movable&);
};
и построить общую фабричную функцию, которая возвращает вновь построенное значение или ссылку на уже построенный объект.
#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;
template <class Factory>
typename Factory::return_type lock_wrapper(Factory f)
{
typedef typename Factory::return_type return_type;
return_type r = f();
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;
}
Предупреждение: При использовании этого макроса в несоответствующих компиляторах или компиляторах C++03 движение будет выполняться даже в том случае, если стандарт C++11 его не допускает (например, возврат статической переменной). Пользователь несет ответственность за использование этого макроса только для возврата локальных объектов, соответствующих критериям C++11. Например:
struct foo
{
copy_movable operator()() const
{
return BOOST_MOVE_RET(copy_movable, cm);
}
static copy_movable cm;
};
Примечание: При возврате временного объекта BOOST_MOVE_REF
не требуется, так как правила эллизионного копирования будут работать как на компиляторах C++03, так и на C++11.
movable get_movable()
{ return movable(); }
copy_movable get_copy_movable()
{ return copy_movable(); }
copyable get_copyable()
{ return copyable(); }