Saya baru saja menemukan diri saya tidak sepenuhnya memahami logika std::move()
.
Awalnya saya googling di Google tapi sepertinya hanya ada dokumen tentang cara penggunaan std::move()
, bukan cara kerja strukturnya.
Maksud saya, saya tahu apa itu fungsi anggota template tetapi ketika saya melihat std::move()
definisi di VS2010, itu masih membingungkan.
definisi dari std :: move () di bawah ini.
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);
}
Yang aneh pertama kali bagi saya adalah parameternya, (_Ty && _Arg), karena ketika saya memanggil fungsi seperti yang Anda lihat di bawah,
// main()
Object obj1;
Object obj2 = std::move(obj1);
itu pada dasarnya sama dengan
// std::move()
_Ty&& _Arg = Obj1;
Tetapi seperti yang telah Anda ketahui, Anda tidak dapat langsung menautkan LValue ke referensi RValue, yang membuat saya berpikir seharusnya seperti ini.
_Ty&& _Arg = (Object&&)obj1;
Namun, ini tidak masuk akal karena std :: move () harus berfungsi untuk semua nilai.
Jadi saya kira untuk sepenuhnya memahami cara kerjanya, saya harus melihat struct ini juga.
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;
};
Sayangnya itu masih membingungkan dan saya tidak mengerti.
Saya tahu bahwa ini semua karena saya kurang memiliki keterampilan sintaks dasar tentang C ++. Saya ingin tahu bagaimana ini bekerja secara menyeluruh dan dokumen apa pun yang bisa saya dapatkan di internet akan disambut dengan sangat baik. (Jika Anda bisa menjelaskan ini, itu akan luar biasa juga).
sumber
move
kerjanya daripada bagaimana penerapannya. Saya menemukan penjelasan ini sangat berguna: pagefault.blog/2018/03/01/… .Jawaban:
Kami mulai dengan fungsi pemindahan (yang saya bersihkan sedikit):
Mari kita mulai dengan bagian yang lebih mudah - yaitu, ketika fungsi tersebut dipanggil dengan rvalue:
dan
move
template kita dibuat sebagai berikut:Karena
remove_reference
mengonversiT&
menjadiT
atauT&&
menjadiT
, danObject
bukan referensi, fungsi terakhir kita adalah:Sekarang, Anda mungkin bertanya-tanya: apakah kita membutuhkan pemerannya? Jawabannya adalah: ya, kami lakukan. Alasannya sederhana; bernama referensi nilai p adalah diperlakukan sebagai lvalue (dan konversi implisit dari lvalue ke referensi nilai p dilarang oleh standar).
Inilah yang terjadi ketika kita memanggil
move
dengan lvalue:dan
move
instansiasi yang sesuai :Sekali lagi,
remove_reference
beralihlahObject&
keObject
dan kami mendapatkan:Sekarang kita sampai pada bagian yang sulit: apa
Object& &&
artinya dan bagaimana itu bisa mengikat ke lvalue?Untuk memungkinkan penerusan yang sempurna, standar C ++ 11 memberikan aturan khusus untuk penciutan referensi, yaitu sebagai berikut:
Seperti yang Anda lihat, di bawah aturan ini
Object& &&
sebenarnya berartiObject&
, yang merupakan referensi nilai l biasa yang memungkinkan mengikat nilai l.Fungsi akhirnya adalah:
yang tidak berbeda dengan contoh sebelumnya dengan rvalue - keduanya melemparkan argumennya ke rvalue referensi dan kemudian mengembalikannya. Perbedaannya adalah bahwa instansiasi pertama hanya dapat digunakan dengan nilai r, sedangkan yang kedua bekerja dengan nilai l.
Untuk menjelaskan mengapa kita membutuhkan
remove_reference
lebih banyak, mari coba fungsi inidan membuat instance dengan lvalue.
Dengan menerapkan aturan penciutan referensi yang disebutkan di atas, Anda dapat melihat kami mendapatkan fungsi yang tidak dapat digunakan sebagai
move
(sederhananya, Anda menyebutnya dengan lvalue, Anda mendapatkan lvalue kembali). Jika ada, fungsi ini adalah fungsi identitas.sumber
T
sebagaiObject&
, saya tidak tahu bahwa ini benar-benar dilakukan. Saya juga berharapT
untuk mengevaluasiObject
dalam kasus ini, karena saya pikir ini adalah alasan untuk memperkenalkan referensi pembungkus danstd::ref
, atau bukan.template <typename T> void f(T arg)
(yang ada di artikel Wikipedia) dantemplate <typename T> void f(T& arg)
. Yang pertama memutuskan untuk menilai (dan jika Anda ingin meneruskan referensi, Anda harus membungkusnyastd::ref
), sedangkan yang kedua selalu memutuskan untuk referensi. Sayangnya, aturan untuk pengurangan argumen template agak rumit, jadi saya tidak bisa memberikan alasan yang tepat mengapaT&&
resovles keObject& &&
(tapi itu memang terjadi).template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
std::move
hanya ingin memberikan nilai l ke nilai r, ya,T&
akan baik-baik saja. Trik ini dilakukan sebagian besar untuk fleksibilitas: Anda dapat memanggilstd::move
semuanya (termasuk nilai r) dan mendapatkan nilai r kembali._Ty adalah parameter template, dan dalam situasi ini
_Ty adalah tipe "Objek &"
itulah mengapa _Remove_reference diperlukan.
Ini akan lebih seperti
Jika kami tidak menghapus referensi, itu akan seperti yang kami lakukan
Tapi ObjectRef && direduksi menjadi Object &, yang tidak bisa kami ikat ke obj2.
Alasan pengurangan cara ini adalah untuk mendukung penerusan sempurna. Lihat makalah ini .
sumber
_Remove_reference_
itu perlu. Misalnya, jika Anda memilikiObject&
sebagai typedef, dan Anda mengacu padanya, Anda masih mendapatkanObject&
. Mengapa itu tidak berhasil dengan &&? Ada adalah jawaban untuk itu, dan itu ada hubungannya dengan forwarding yang sempurna.