Po prostu stwierdziłem, że nie w pełni rozumiem logikę std::move()
.
Na początku wyszukałem go w Google, ale wydaje się, że są tylko dokumenty o tym, jak używać std::move()
, a nie o tym , jak działa jego struktura.
Chodzi mi o to, że wiem, jaka jest funkcja elementu członkowskiego szablonu, ale kiedy patrzę na std::move()
definicję w VS2010, nadal jest myląca.
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);
}
Przede wszystkim dziwny jest dla mnie parametr (_Ty && _Arg), ponieważ kiedy wywołuję funkcję, jak widać 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żna bezpośrednio powiązać LValue z odniesieniem 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 również przyjrzeć się 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 to zagmatwane i nie rozumiem.
Wiem, że to wszystko z powodu mojego braku podstawowych umiejętności składniowych w C ++. Chciałbym wiedzieć, jak dokładnie działają, a wszelkie dokumenty, które mogę znaleźć w Internecie, będą mile widziane. (Jeśli możesz to tylko wyjaśnić, to też będzie niesamowite).
źródło
move
działa, a nie jak jest wdrażane. Uważam, że to wyjaśnienie jest naprawdę przydatne: pagefault.blog/2018/03/01/… .Odpowiedzi:
Zaczynamy od funkcji move (którą trochę wyczyściłem):
Zacznijmy od łatwiejszej części - czyli gdy funkcja jest wywoływana z rvalue:
a nasz
move
szablon zostanie utworzony w następujący sposób:Ponieważ
remove_reference
konwertujeT&
doT
lubT&&
doT
iObject
nie jest odniesieniem, nasza ostatnia funkcja to:Teraz możesz się zastanawiać: czy w ogóle potrzebujemy obsady? Odpowiedź brzmi: tak, robimy. Powód jest prosty; nazwane odwołanie do wartości r jest traktowane jako lwartość (a niejawna konwersja z lwartości na odwołanie do r wartości jest standardowo zabroniona).
Oto, co się dzieje, gdy wywołujemy
move
z lvalue:i odpowiednia
move
instancja:Ponownie
remove_reference
konwertujemyObject&
naObject
i otrzymujemy:Teraz przechodzimy do trudnej części: co to w
Object& &&
ogóle oznacza i jak może wiązać się z lwartością?Aby umożliwić idealne przekazywanie, standard C ++ 11 zapewnia specjalne reguły zwijania referencji, które są następujące:
Jak widać, zgodnie z tymi regułami
Object& &&
faktycznie oznacza toObject&
, że jest to zwykłe odniesienie do lwartości, które umożliwia wiązanie l-wartości.Ostateczna funkcja to:
co nie różni się od poprzedniego wystąpienia z rvalue - oba rzutują swój argument na odwołanie do rvalue, a następnie zwracają go. Różnica polega na tym, że pierwsza instancja może być używana tylko z rvalues, podczas gdy druga działa z lvalues.
Aby wyjaśnić, dlaczego potrzebujemy
remove_reference
trochę więcej, wypróbujmy tę funkcjęi utwórz instancję z lvalue.
Stosując wspomniane powyżej reguły zwijania referencji, możesz zobaczyć, że otrzymujemy funkcję, która jest bezużyteczna jako
move
(mówiąc prościej, wywołujesz ją z lvalue, otrzymujesz lvalue z powrotem). Jeśli już, ta funkcja jest funkcją tożsamości.źródło
T
jakoObject&
, nie wiedziałem, że tak się dzieje. SpodziewałbymT
się równieżObject
w tym przypadku, ponieważ myślałem, że to jest powód wprowadzenia referencji opakowujących istd::ref
, czy nie było.template <typename T> void f(T arg)
(co jest w artykule w Wikipedii) itemplate <typename T> void f(T& arg)
. Pierwsza z nich oznacza wartość (i jeśli chcesz przekazać referencję, musisz ją zawinąćstd::ref
), podczas gdy druga zawsze jest rozpoznawana jako referencja. Niestety, zasady dedukcji argumentów z szablonu są dość złożone, więc nie mogę podać dokładnego uzasadnienia, dlaczegoT&&
tak sięObject& &&
dzieje (ale tak się dzieje).template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
std::move
tylko rzucać lvalues na rvalues, to tak,T&
byłoby w porządku. Ta sztuczka jest wykonywana głównie dla elastyczności: możesz wywołaćstd::move
wszystko (włączając wartości r) i odzyskać rwartość._Ty to parametr szablonu iw tej sytuacji
_Ty jest typu „Object &”
dlatego konieczne jest _Remove_reference.
Byłoby bardziej jak
Gdybyśmy nie usunęli odniesienia, wyglądałoby to tak, jak robiliśmy
Ale ObjectRef && redukuje się do Object &, którego nie mogliśmy powiązać z obj2.
Powodem, dla którego zmniejsza się w ten sposób, jest wspieranie doskonałego przekazywania. Zobacz ten artykuł .
źródło
_Remove_reference_
jest to konieczne. Na przykład, jeśli maszObject&
typedef i weźmiesz do niego odniesienie, nadal otrzymujeszObject&
. Dlaczego to nie działa z &&? Tam to odpowiedź i ma to związek z perfekcyjnym przekazywaniem.