Saya telah melihat beberapa fitur baru C ++ 11 dan yang saya perhatikan adalah double ampersand dalam mendeklarasikan variabel, seperti T&& var
.
Sebagai permulaan, apa nama binatang ini? Saya berharap Google mengizinkan kami untuk mencari tanda baca seperti ini.
Apa tepatnya yang dilakukan artinya?
Pada pandangan pertama, ini tampaknya menjadi referensi ganda (seperti pointer ganda C-style T** var
), tapi saya kesulitan memikirkan use case untuk itu.
c++
c++11
rvalue-reference
c++-faq
perfect-forwarding
paxdiablo
sumber
sumber
:)
Jawaban:
Ini menyatakan referensi nilai (proposal standar dokumen).
Inilah pengantar untuk menilai kembali referensi .
Berikut ini adalah tinjauan mendalam yang luar biasa pada referensi nilai oleh salah satu pengembang perpustakaan standar Microsoft .
Perbedaan terbesar antara referensi C ++ 03 (sekarang disebut referensi lvalue dalam C ++ 11) adalah bahwa ia dapat mengikat ke nilai seperti sementara tanpa harus menjadi const. Dengan demikian, sintaks ini sekarang sah:
referensi nilai terutama untuk hal-hal berikut:
Pindahkan semantik . Operator pemindah dan pemindah tugas sekarang dapat didefinisikan yang mengambil referensi nilai daripada referensi konstanta-nilai yang biasa. Pindah berfungsi seperti salinan, kecuali tidak wajib menjaga sumbernya tidak berubah; bahkan, biasanya memodifikasi sumber sehingga tidak lagi memiliki sumber daya yang dipindahkan. Ini bagus untuk menghilangkan salinan asing, terutama dalam implementasi perpustakaan standar.
Misalnya, penyalin salinan mungkin terlihat seperti ini:
Jika konstruktor ini disahkan sementara, salinannya tidak perlu karena kami tahu sementara hanya akan dihancurkan; mengapa tidak menggunakan sumber daya yang sementara telah dialokasikan? Di C ++ 03, tidak ada cara untuk mencegah penyalinan karena kami tidak dapat menentukan bahwa kami lulus sementara. Di C ++ 11, kita bisa membebani konstruktor pemindahan:
Perhatikan perbedaan besar di sini: move constructor sebenarnya memodifikasi argumennya. Ini secara efektif akan "memindahkan" sementara ke objek yang sedang dibangun, sehingga menghilangkan salinan yang tidak perlu.
Konstruktor pemindahan akan digunakan untuk temporari dan untuk referensi non-nilai konstanta yang secara eksplisit dikonversi ke referensi nilai menggunakan
std::move
fungsi (itu hanya melakukan konversi). Kode berikut keduanya memohon konstruktor pemindahan untukf1
danf2
:Penerusan sempurna . referensi nilai memungkinkan kita untuk meneruskan argumen dengan benar untuk fungsi templated. Ambil contoh fungsi pabrik ini:
Jika kita memanggil
factory<foo>(5)
, argumen akan dideduksi menjadiint&
, yang tidak akan mengikat ke 5 literal, bahkan jikafoo
konstruktor membutuhkanint
. Yah, kita malah bisa menggunakanA1 const&
, tetapi bagaimana jikafoo
mengambil argumen konstruktor dengan referensi non-const? Untuk membuat fungsi pabrik yang benar-benar generik, kita harus membebani pabrik diA1&
dan diA1 const&
. Itu mungkin baik-baik saja jika pabrik mengambil 1 tipe parameter, tetapi setiap tipe parameter tambahan akan melipatgandakan kelebihan yang diperlukan ditetapkan oleh 2. Itu sangat cepat tidak dapat dipertahankan.referensi nilai memperbaiki masalah ini dengan memungkinkan perpustakaan standar untuk mendefinisikan
std::forward
fungsi yang dapat meneruskan referensi nilai / nilai dengan benar. Untuk informasi lebih lanjut tentang carastd::forward
kerjanya, lihat jawaban yang sangat bagus ini .Ini memungkinkan kami untuk mendefinisikan fungsi pabrik seperti ini:
Sekarang argumen / nilai-nilai argumen dipertahankan ketika diteruskan ke
T
konstruktor. Itu berarti bahwa jika pabrik dipanggil dengan nilai,T
konstruktor disebut dengan nilai. Jika pabrik dipanggil dengan lvalue,T
konstruktor disebut dengan lvalue. Fungsi pabrik yang ditingkatkan berfungsi karena satu aturan khusus:Jadi, kita bisa menggunakan pabrik seperti ini:
Properti referensi nilai penting :
float f = 0f; int&& i = f;
terbentuk dengan baik karena float secara implisit dapat dikonversi menjadi int; rujukannya adalah sementara yang merupakan hasil konversi.std::move
panggilan itu perlu di:foo&& r = foo(); foo f = std::move(r);
sumber
Named rvalue references are lvalues. Unnamed rvalue references are rvalues.
; tanpa mengetahui hal ini saya telah berjuang untuk memahami mengapa orang melakukan halT &&t; std::move(t);
yang lama dalam memindahkan petugas, dan sejenisnya.int x; int &&rrx = x;
tidak lagi dikompilasi dalam GCC)typename identity<T>::type& a
setara denganT&
?Ini menunjukkan referensi nilai. Referensi nilai hanya akan mengikat ke objek sementara, kecuali secara eksplisit dihasilkan sebaliknya. Mereka digunakan untuk membuat objek jauh lebih efisien dalam keadaan tertentu, dan untuk menyediakan fasilitas yang dikenal sebagai penerusan yang sempurna, yang sangat menyederhanakan kode templat.
Di C ++ 03, Anda tidak bisa membedakan antara salinan nilai yang tidak dapat diubah dan nilai yang lain.
Dalam C ++ 0x, ini tidak terjadi.
Pertimbangkan implementasi di balik konstruktor ini. Dalam kasus pertama, string harus melakukan salinan untuk mempertahankan nilai semantik, yang melibatkan alokasi tumpukan baru. Namun, dalam kasus kedua, kita tahu sebelumnya bahwa objek yang diteruskan ke konstruktor kita segera karena kehancuran, dan itu tidak harus tetap tak tersentuh. Kami dapat secara efektif hanya menukar pointer internal dan tidak melakukan penyalinan sama sekali dalam skenario ini, yang jauh lebih efisien. Memindahkan semantik menguntungkan setiap kelas yang mahal atau melarang penyalinan sumber daya yang dirujuk secara internal. Pertimbangkan kasus
std::unique_ptr
- sekarang kelas kita dapat membedakan antara temporaries dan non-temporaries, kita dapat membuat gerakan semantik bekerja dengan benar sehinggaunique_ptr
tidak dapat disalin tetapi dapat dipindahkan, yang berarti bahwastd::unique_ptr
dapat disimpan secara legal dalam kontainer Standar, disortir, dll, sedangkan C ++ 03std::auto_ptr
tidak bisa.Sekarang kami mempertimbangkan penggunaan lain dari referensi nilai-penerusan sempurna. Pertimbangkan pertanyaan tentang mengikat referensi ke referensi.
Tidak dapat mengingat apa yang dikatakan C ++ 03 tentang ini, tetapi dalam C ++ 0x, tipe yang dihasilkan ketika berhadapan dengan referensi nilai adalah sangat penting. Referensi nilai untuk tipe T, di mana T adalah tipe referensi, menjadi referensi tipe T.
Pertimbangkan fungsi template paling sederhana - minimal dan maks. Dalam C ++ 03 Anda harus membebani semua kombinasi const dan non-const secara manual. Dalam C ++ 0x itu hanya satu kelebihan. Dikombinasikan dengan templat variadic, ini memungkinkan penerusan yang sempurna.
Saya mengabaikan pengurangan jenis pengembalian, karena saya tidak dapat mengingat bagaimana hal itu dilakukan begitu saja, tetapi min itu dapat menerima kombinasi dari setiap nilai, nilai, dan nilai.
sumber
std::forward<A>(aref) < std::forward<B>(bref)
? dan saya tidak berpikir definisi ini akan benar ketika Anda mencoba majuint&
danfloat&
. Lebih baik jatuhkan satu templat formulir tipe.Istilah untuk
T&&
saat digunakan dengan pengurangan tipe (seperti untuk penerusan yang sempurna) dikenal dengan bahasa sehari-hari sebagai referensi penerusan . Istilah "referensi universal" diciptakan oleh Scott Meyers dalam artikel ini , tetapi kemudian diubah.Itu karena mungkin nilai-r atau nilai-l.
Contohnya adalah:
Diskusi lebih lanjut dapat ditemukan dalam jawaban untuk: Sintaks untuk referensi universal
sumber
Referensi nilai adalah jenis yang berperilaku seperti referensi biasa X &, dengan beberapa pengecualian. Yang paling penting adalah bahwa ketika datang ke fungsi resolusi overload, nilai-nilai lebih suka referensi nilai-gaya lama, sedangkan nilai-nilai lebih suka referensi nilai-nilai baru:
Jadi apa itu nilai? Apa pun yang bukan nilai. Nilai lebih merupakan ekspresi yang merujuk ke lokasi memori dan memungkinkan kami untuk mengambil alamat lokasi memori itu melalui operator &.
Hampir lebih mudah untuk memahami terlebih dahulu apa yang dicapai oleh nilai dengan contoh:
Operator konstruktor dan tugas telah kelebihan beban dengan versi yang mengambil referensi nilai. Referensi nilai memungkinkan fungsi untuk bercabang pada waktu kompilasi (melalui resolusi overload) dengan syarat "Apakah saya dipanggil pada nilai lebih atau nilai?". Ini memungkinkan kami untuk membuat operator konstruktor dan penugasan yang lebih efisien di atas yang memindahkan sumber daya alih-alih menyalinnya.
Compiler secara otomatis bercabang pada waktu kompilasi (tergantung pada apakah ia dipanggil untuk nilai lebih atau nilai) memilih apakah operator konstruktor atau pemindah tugas harus dipanggil.
Kesimpulannya: referensi nilai memungkinkan semantik bergerak (dan penerusan sempurna, dibahas di tautan artikel di bawah).
Salah satu contoh praktis yang mudah dipahami adalah template kelas std :: unique_ptr . Karena unique_ptr mempertahankan kepemilikan eksklusif dari pointer mentah yang mendasarinya, unique_ptr tidak dapat disalin. Itu akan melanggar invarian kepemilikan eksklusif mereka. Jadi mereka tidak memiliki copy constructor. Tetapi mereka memiliki konstruktor bergerak:
static_cast<unique_ptr<int[]>&&>(ptr)
biasanya dilakukan menggunakan std :: moveArtikel yang sangat bagus menjelaskan semua ini dan banyak lagi (seperti bagaimana nilai memungkinkan penerusan sempurna dan apa artinya itu) dengan banyak contoh bagus adalah Dijelaskan oleh Thomas Becker . Posting ini sangat bergantung pada artikelnya.
Pengantar yang lebih pendek adalah Pengantar Singkat untuk Menilai Nilai oleh Stroutrup, et. Al
sumber
Sample(const Sample& s)
juga konstruktor copy perlu menyalin konten? Pertanyaan yang sama untuk 'operator penugasan salinan'.