Подтвердить что ты не робот

Как std:: move() переносит значения в RValues?

Я просто обнаружил, что не полностью понимаю логику std::move().

Во-первых, я googled, но похоже, что есть только документы о том, как использовать std::move(), а не как работает его структура.

Я имею в виду, я знаю, что такое функция члена шаблона, но когда я смотрю в определение std::move() в VS2010, он все еще запутан.

определение std:: move() идет ниже.

template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
    move(_Ty&& _Arg)
    {   // forward _Arg as movable
        return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
    }

Что для меня странно, это параметр, (_Ty && _Arg), потому что, когда я вызываю функцию, как вы видите ниже,

// main()
Object obj1;
Object obj2 = std::move(obj1);

он в основном равен

// std::move()
_Ty&& _Arg = Obj1;

Но, как вы уже знаете, вы не можете напрямую связать LValue с ссылкой RValue, что заставляет меня думать, что это должно быть так.

_Ty&& _Arg = (Object&&)obj1;

Однако это абсурдно, потому что std:: move() должен работать для всех значений.

Итак, я догадываюсь, чтобы полностью понять, как это работает, я должен также взглянуть на эти структуры.

template<class _Ty>
struct _Remove_reference
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&>
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&&>
{   // remove rvalue reference
    typedef _Ty _Type;
};

К сожалению, он все еще запутан, и я не понимаю.

Я знаю, что это все из-за отсутствия базовых синтаксических навыков в С++. Я хотел бы знать, как эти работы тщательно и любые документы, которые я могу получить в Интернете, будут более чем приветствуемыми. (Если вы можете просто объяснить это, это тоже будет потрясающе).

4b9b3361

Ответ 1

Начнем с функции перемещения (которую я немного почистил):

template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
  return static_cast<typename remove_reference<T>::type&&>(arg);
}

Давайте начнем с более простой части - то есть, когда функция вызывается с rvalue:

Object a = std::move(Object());
// Object() is temporary, which is prvalue

и наш шаблон move получает экземпляр следующим образом:

// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
  return static_cast<remove_reference<Object>::type&&>(arg);
}

Так как remove_reference преобразует T& в T или T&& в T, а Object не является ссылкой, наша конечная функция:

Object&& move(Object&& arg)
{
  return static_cast<Object&&>(arg);
}

Теперь вы можете подумать: нам даже нужен актерский состав? Ответ: да, мы делаем. Причина проста; указанная ссылка rvalue рассматривается как lvalue (и неявное преобразование от lvalue к rvalue-ссылке запрещено стандартом).


Вот что происходит, когда мы вызываем move с lvalue:

Object a; // a is lvalue
Object b = std::move(a);

и соответствующий move экземпляр:

// move with [T = Object&]
remove_reference<Object&>::type&& move(Object& && arg)
{
  return static_cast<remove_reference<Object&>::type&&>(arg);
}

Опять же, remove_reference преобразует Object& в Object и мы получаем:

Object&& move(Object& && arg)
{
  return static_cast<Object&&>(arg);
}

Теперь мы перейдем к сложной части: что означает Object& && и как оно может связываться с lvalue?

Чтобы обеспечить идеальную пересылку, стандарт С++ 11 предоставляет специальные правила для свертывания ссылок, которые заключаются в следующем:

Object &  &  = Object &
Object &  && = Object &
Object && &  = Object &
Object && && = Object &&

Как вы можете видеть, в соответствии с этими правилами Object& && на самом деле означает Object&, который является простой ссылкой lvalue, которая позволяет связывать lvalues.

Конечная функция:

Object&& move(Object& arg)
{
  return static_cast<Object&&>(arg);
}

который не похож на предыдущий экземпляр с rvalue - они оба передают свой аргумент в rvalue reference, а затем возвращают его. Разница в том, что первое экземплярирование можно использовать только с rvalues, а второе - с lvalues.


Чтобы объяснить, зачем нам нужно remove_reference немного больше, попробуйте эту функцию

template <typename T>
T&& wanna_be_move(T&& arg)
{
  return static_cast<T&&>(arg);
}

и создайте его с помощью lvalue.

// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
  return static_cast<Object& &&>(arg);
}

Применяя указанные выше правила сбрасывания ссылок, вы можете увидеть, что мы получаем функцию, которая непригодна для использования в качестве move (просто, вы называете ее lvalue, вы получаете lvalue назад). Во всяком случае, эта функция является функцией тождества.

Object& wanna_be_move(Object& arg)
{
  return static_cast<Object&>(arg);
}

Ответ 2

_Ty является параметром шаблона, и в этой ситуации

Object obj1;
Object obj2 = std::move(obj1);

_Ty - это тип "Object &"

поэтому необходимо _Remove_reference.

Это будет больше похоже на

typedef Object& ObjectRef;
Object obj1;
ObjectRef&& obj1_ref = obj1;
Object&& obj2 = (Object&&)obj1_ref;

Если бы мы не удалили ссылку, это было бы похоже на то, что мы делали

Object&& obj2 = (ObjectRef&&)obj1_ref;

Но ObjectRef && сводится к Object &, который мы не могли бы связывать с obj2.

Причина, по которой это происходит, заключается в поддержке идеальной пересылки. См. этот документ.