Mengapa ada kebutuhan untuk memiliki std::reference_wrapper
? Dimana harus digunakan? Apa bedanya dengan penunjuk sederhana? Bagaimana performanya dibandingkan dengan pointer sederhana?
99
Mengapa ada kebutuhan untuk memiliki std::reference_wrapper
? Dimana harus digunakan? Apa bedanya dengan penunjuk sederhana? Bagaimana performanya dibandingkan dengan pointer sederhana?
.
dengan alih-alih->
.
tidak berfungsi seperti yang Anda sarankan (kecuali pada titik tertentu proposal titik operator diadopsi dan diintegrasikan :))get()
fungsi-anggotanya atau dengan konversi implisitnya kembali ke tipe yang mendasarinya.Jawaban:
std::reference_wrapper
berguna jika dikombinasikan dengan template. Ini membungkus objek dengan menyimpan penunjuk ke sana, memungkinkan untuk penugasan ulang dan menyalin sambil meniru semantik biasanya. Ini juga menginstruksikan templat perpustakaan tertentu untuk menyimpan referensi, bukan objek.Pertimbangkan algoritme di STL yang menyalin functor: Anda dapat menghindari penyalinan itu hanya dengan meneruskan pembungkus referensi yang merujuk ke functor alih-alih functor itu sendiri:
Ini berfungsi karena…
…
reference_wrapper
S overloadoperator()
sehingga mereka dapat dipanggil seperti objek fungsi yang dirujuknya:… (Tidak) seperti referensi biasa, menyalin (dan menetapkan)
reference_wrappers
hanya menugaskan orang yang ditunjuk.Menyalin pembungkus referensi secara praktis sama dengan menyalin penunjuk, yang semurah harganya. Semua pemanggilan fungsi yang melekat dalam penggunaannya (misalnya yang ke
operator()
) harus hanya sebaris karena merupakan satu baris.reference_wrapper
s dibuat melaluistd::ref
danstd::cref
:Argumen template menentukan tipe dan kualifikasi cv dari objek yang dirujuk;
r2
mengacu pada aconst int
dan hanya akan menghasilkan referensi keconst int
. Panggilan ke pembungkus referensi dengan fungsiconst
di dalamnya hanya akan memanggilconst
fungsi anggotaoperator()
.Rvalue penginisialisasi tidak diizinkan, karena mengizinkannya akan lebih merugikan daripada menguntungkan. Karena rvalues akan dipindahkan bagaimanapun juga (dan dengan penghapusan salinan yang dijamin bahkan itu dihindari sebagian), kami tidak meningkatkan semantik; kami dapat memperkenalkan petunjuk yang menjuntai, karena pembungkus referensi tidak memperpanjang umur penerima.
Interaksi perpustakaan
Seperti disebutkan sebelumnya, seseorang dapat menginstruksikan
make_tuple
untuk menyimpan referensi dalam hasiltuple
dengan meneruskan argumen terkait melaluireference_wrapper
:Perhatikan bahwa ini sedikit berbeda dari
forward_as_tuple
: Di sini, nilai r sebagai argumen tidak diperbolehkan.std::bind
menunjukkan perilaku yang sama: Ini tidak akan menyalin argumen tetapi menyimpan referensi jika itu adalahreference_wrapper
. Berguna jika argumen itu (atau functor!) Tidak perlu disalin tetapi tetap dalam ruang lingkup sementarabind
-functor digunakan.Perbedaan dari petunjuk biasa
Tidak ada tingkat tambahan dari tipuan sintaksis. Pointer harus direferensikan untuk mendapatkan nilai l ke objek yang dirujuk;
reference_wrapper
s memiliki operator konversi implisit dan dapat dipanggil seperti objek yang dibungkusnya.reference_wrapper
s, tidak seperti pointer, tidak memiliki status null. Mereka harus diinisialisasi dengan referensi atau lainnyareference_wrapper
.Kesamaannya adalah semantik salinan yang dangkal: Pointer dan
reference_wrapper
s dapat ditetapkan ulang.sumber
std::make_tuple(std::ref(i));
lebih unggulstd::make_tuple(&i);
dalam beberapa hal?i
, bukan referensi ke sana.Setidaknya ada dua tujuan motivasi dari
std::reference_wrapper<T>
:Ini untuk memberikan semantik referensi ke objek yang diteruskan sebagai parameter nilai ke template fungsi. Misalnya, Anda mungkin memiliki objek fungsi besar yang ingin Anda teruskan
std::for_each()
yang mengambil parameter objek fungsinya berdasarkan nilai. Untuk menghindari penyalinan objek, Anda dapat menggunakanMelewati argumen sebagai
std::reference_wrapper<T>
sebuahstd::bind()
ekspresi cukup umum untuk mengikat argumen dengan referensi bukan oleh nilai.Saat menggunakan
std::reference_wrapper<T>
denganstd::make_tuple()
elemen tupel yang sesuai menjadiT&
bukanT
:sumber
fun
adalah objek fungsi (yaitu objek kelas dengan operator pemanggil fungsi) dan bukan fungsi: jikafun
kebetulan merupakan fungsi sebenarnya,std::ref(fun)
tidak memiliki tujuan dan membuat kode berpotensi lebih lambat.Perbedaan lain, dalam hal kode yang mendokumentasikan diri, adalah bahwa menggunakan pada
reference_wrapper
dasarnya mengingkari kepemilikan objek. Sebaliknya, aunique_ptr
menegaskan kepemilikan, sementara pointer kosong mungkin atau mungkin tidak dimiliki (tidak mungkin untuk mengetahuinya tanpa melihat banyak kode terkait):sumber
reference_wrapper
lebih unggul dari petunjuk mentah tidak hanya karena jelas bahwa itu bukan kepemilikan, tetapi juga karena tidak dapatnullptr
(tanpa kejahatan) dan dengan demikian pengguna tahu mereka tidak dapat lewatnullptr
(tanpa kejahatan) dan Anda tahu Anda tidak perlu melakukannya periksa itu.Anda bisa menganggapnya sebagai pembungkus praktis di sekitar referensi sehingga Anda bisa menggunakannya dalam wadah.
Ini pada dasarnya adalah
CopyAssignable
versiT&
. Kapan pun Anda menginginkan referensi, tetapi harus dapat dialihkan, gunakanstd::reference_wrapper<T>
atau fungsi penolongnyastd::ref()
. Atau gunakan penunjuk.Kebiasaan lainnya
sizeof
:Dan perbandingan:
sumber
reference_wrapper
kode sepele , membuatnya identik dengan kode yang menggunakan pointer atau referensi.std::reference_wrapper
memiliki jaminan bahwa objek tersebut tidak pernah nol. Pertimbangkan seorang anggota kelasstd::vector<T *>
. Anda harus memeriksa semua kode kelas untuk melihat apakah objek ini dapat menyimpan anullptr
dalam vektor, sedangkan denganstd::reference_wrapper<T>
, Anda dijamin memiliki objek yang valid.