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 .
PERHATIAN: artikel tertaut di MSDN ("Rvalue Referensi: C ++ 0x Fitur di VC10, Bagian 2") adalah pengantar yang sangat jelas untuk referensi Rvalue, tetapi membuat pernyataan tentang Rvalue referensi yang dulunya benar dalam konsep C ++ 11 standar, tetapi tidak benar untuk yang terakhir! Secara khusus, dikatakan di berbagai titik bahwa nilai referensi dapat mengikat nilai-nilai, yang pernah benar, tetapi diubah (mis. Int x; int && rrx = x; tidak lagi dikompilasi dalam GCC) - drewbarbs 13 Juli 14 di 16:12
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:
T&& r = T();
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:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
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:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
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 untuk f1
dan f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Penerusan sempurna . referensi nilai memungkinkan kita untuk meneruskan argumen dengan benar untuk fungsi templated. Ambil contoh fungsi pabrik ini:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Jika kita memanggil factory<foo>(5)
, argumen akan dideduksi menjadi int&
, yang tidak akan mengikat ke 5 literal, bahkan jika foo
konstruktor membutuhkan int
. Yah, kita malah bisa menggunakan A1 const&
, tetapi bagaimana jika foo
mengambil argumen konstruktor dengan referensi non-const? Untuk membuat fungsi pabrik yang benar-benar generik, kita harus membebani pabrik di A1&
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 cara std::forward
kerjanya, lihat jawaban yang sangat bagus ini .
Ini memungkinkan kami untuk mendefinisikan fungsi pabrik seperti ini:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
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:
Ketika tipe parameter fungsi adalah formulir di T&&
mana T
merupakan parameter templat, dan argumen fungsi adalah nilai jenis A
, maka jenis A&
ini digunakan untuk pengurangan argumen templat.
Jadi, kita bisa menggunakan pabrik seperti ini:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Properti referensi nilai penting :
- Untuk resolusi kelebihan beban, nilai lebih suka mengikat ke nilai referensi dan nilai lebih suka mengikat ke nilai referensi . Oleh karena itu mengapa temporaries lebih suka memanggil move constructor / move assignment operator daripada copy constructor / assignment operator
- referensi nilai akan secara implisit mengikat nilai dan untuk sementara yang merupakan hasil dari konversi implisit . yaitu
float f = 0f; int&& i = f;
terbentuk dengan baik karena float secara implisit dapat dikonversi menjadi int; rujukannya adalah sementara yang merupakan hasil konversi.
- Referensi nilai yang dinamai adalah nilai. Referensi nilai yang tidak disebutkan namanya adalah nilai. Ini penting untuk memahami mengapa
std::move
panggilan itu perlu di:foo&& r = foo(); foo f = std::move(r);