Jak std:: move() przenosi wartości do wartości R?

Okazało się, że nie do końca rozumiem logikę std::move().

Na początku wygooglowałem go, ale wygląda na to, że są tylko dokumenty o tym, jak używać std::move(), a nie jak działa jego struktura.

To znaczy, wiem co to jest funkcja member szablonu, ale kiedy patrzę na definicję std::move() W VS2010, to nadal jest mylące.

Definicja STD:: move() znajduje się poniżej.

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);
    }

Dziwne jest dla mnie najpierw parametr (_Ty&& _Arg), bo gdy wywołuję funkcję jak widzisz poniżej,

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

W zasadzie równa się

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

Ale jak już wiesz, nie możesz bezpośrednio połączyć LValue z referencją RValue, co sprawia, że myślę, że powinno tak być.

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

Jest to jednak absurdalne, ponieważ std:: move () musi działać dla wszystkich wartości.

Więc myślę, że aby w pełni zrozumieć, jak to działa, powinienem przyjrzeć się również tym strukturom.

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;
};

Niestety nadal jest mylące i nie rozumiem tego.

I wiedz, że to wszystko z powodu mojego braku podstawowych umiejętności w zakresie składni C++. Chciałbym wiedzieć, jak to dokładnie działa, a wszelkie dokumenty, które mogę uzyskać w Internecie, będą mile widziane. (Jeśli możesz to wyjaśnić, to też będzie niesamowite).

Author: bitek, 2011-09-22

2 answers

Zaczynamy od funkcji move (którą trochę posprzątałem):

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

Zacznijmy od łatwiejszej części - czyli gdy funkcja jest wywoływana z rvalue:

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

I nasz move szablon zostanie utworzony w następujący sposób:

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

Ponieważ remove_reference konwertuje T& na T lub T&& na T, A Object nie jest referencją, nasza ostateczna funkcja to:

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

Teraz możecie się zastanawiać: czy potrzebujemy w ogóle obsady? Odpowiedź brzmi: tak, mamy. Na powód jest prosty; o nazwie rvalue reference jest traktowane jako lvalue (a domyślna konwersja z lvalue do rvalue reference jest zabroniona przez standard).


Oto co się dzieje, gdy wywołujemy move z lvalue:

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

I odpowiadające move instancja:

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

Ponownie, remove_reference zamienia Object& na Object i otrzymujemy:

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

Teraz przejdźmy do trudnej części: co w ogóle oznacza Object& && i jak może wiązać się z lvalue?

Aby umożliwić perfect forwarding, standard C++11 zawiera specjalne reguły dla zawalania referencji, które są następujące:

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

Jak widzisz, zgodnie z tymi zasadami Object& && W rzeczywistości oznacza Object& , co jest prostym odniesieniem do lvalue, które umożliwia wiązanie lvalue.

Funkcja końcowa jest zatem:

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

Co nie różni się od poprzedniej instancji z rvalue - obie rzucają jej argument do referencji rvalue, a następnie zwracają ją. Różnica polega na tym, że pierwsza instancja może być używana z tylko wartości R, podczas gdy druga działa z wartościami LV.


Aby wyjaśnić, dlaczego potrzebujemy remove_reference trochę więcej, spróbujmy tej funkcji

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

I utwórz instancję za pomocą lvalue.

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

Stosując wspomniane wyżej reguły zwijania referencji, widzimy, że otrzymujemy funkcję, która jest bezużyteczna jako move (Mówiąc prościej, wywołujemy ją za pomocą lvalue, otrzymujemy lvalue z powrotem). Jeśli już, to funkcja ta jest funkcją tożsamościową.

Object& wanna_be_move(Object& arg)
{
  return static_cast<Object&>(arg);
}
 125
Author: Vitus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-20 17:03:06

_Ty jest parametrem szablonu, a w tej sytuacji

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

_ty jest typu "obiekt &"

Dlatego konieczna jest _Remove_reference.

Byłoby bardziej jak

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

Gdybyśmy nie usunęli odnośnika, to byłoby tak, jakbyśmy robili

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

Ale ObjectRef & & redukuje do Object&, którego nie mogliśmy powiązać z obj2.

Powodem, dla którego redukuje ten sposób, jest obsługa doskonałego przekierowania. Zobacz Ten artykuł .

 2
Author: Vaughn Cato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-22 07:04:47