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