Dengan menggunakan auto&& var = <initializer>
Anda mengatakan: Saya akan menerima inisialisasi apa pun terlepas dari apakah itu adalah ekspresi nilai rendah atau nilai dan saya akan menjaga keteguhannya . Ini biasanya digunakan untuk penerusan (biasanya dengan T&&
). Alasan ini berhasil adalah karena "referensi universal", auto&&
atau T&&
, akan mengikat apa pun .
Anda mungkin berkata, mengapa tidak hanya menggunakan const auto&
karena itu juga akan mengikat apa pun? Masalah dengan menggunakan const
referensi adalah itu const
! Anda tidak akan dapat kemudian mengikatnya ke referensi non-const atau memohon fungsi anggota yang tidak ditandai const
.
Sebagai contoh, bayangkan Anda ingin mendapatkan std::vector
, ambil iterator ke elemen pertama dan modifikasi nilai yang ditunjukkan oleh iterator dengan cara:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
Kode ini akan dikompilasi dengan baik terlepas dari ekspresi initializer. Alternatif untuk auto&&
gagal dengan cara berikut:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
Jadi untuk ini, auto&&
bekerja dengan sempurna! Contoh menggunakan auto&&
seperti ini adalah dalam for
lingkaran berbasis rentang . Lihat pertanyaan saya yang lain untuk lebih jelasnya.
Jika kemudian Anda gunakan std::forward
pada auto&&
referensi Anda untuk mempertahankan fakta bahwa itu awalnya baik nilai atau nilai, kode Anda mengatakan: Sekarang saya sudah mendapatkan objek Anda dari ekspresi nilai atau nilai, saya ingin mempertahankan mana pun yang menilai itu awalnya sudah jadi saya bisa menggunakannya paling efisien - ini mungkin membatalkannya. Seperti dalam:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
Hal ini memungkinkan use_it_elsewhere
untuk menghilangkan nyali untuk kinerja (menghindari salinan) ketika penginisialisasi asli adalah nilai yang dapat dimodifikasi.
Apa artinya ini apakah kita bisa atau kapan kita bisa mencuri sumber daya var
? Yah karena auto&&
kehendak mengikat apa pun, kita tidak mungkin mencoba untuk merobek var
nyali diri kita sendiri - itu mungkin nilai yang lebih baik atau bahkan const. Namun kita bisa std::forward
ke fungsi lain yang benar-benar dapat merusak bagian dalamnya. Segera setelah kami melakukan ini, kami harus mempertimbangkan var
dalam keadaan tidak valid.
Sekarang mari kita terapkan ini pada kasus auto&& var = foo();
, seperti yang diberikan dalam pertanyaan Anda, di mana foo mengembalikan T
nilai berdasarkan. Dalam hal ini kita tahu pasti bahwa jenis var
akan disimpulkan sebagai T&&
. Karena kita tahu pasti bahwa itu adalah nilai, kita tidak perlu std::forward
izin untuk mencuri sumber dayanya. Dalam kasus khusus ini, mengetahui bahwa foo
kembali dengan nilai , pembaca harus membacanya sebagai: Saya mengambil referensi nilai untuk sementara kembali dari foo
, jadi saya bisa dengan senang hati pindah dari itu.
Sebagai tambahan, saya pikir ada baiknya menyebutkan kapan ekspresi seperti some_expression_that_may_be_rvalue_or_lvalue
mungkin muncul, selain situasi "baik kode Anda mungkin berubah". Jadi, inilah contoh yang dibuat:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
Di sini, get_vector<T>()
adalah ekspresi indah yang bisa berupa nilai atau nilai tergantung pada jenis generik T
. Kami pada dasarnya mengubah jenis pengembalian get_vector
melalui parameter templat foo
.
Ketika kami menelepon foo<std::vector<int>>
, get_vector
akan kembali global_vec
dengan nilai, yang memberikan ekspresi nilai. Atau, saat kita menelepon foo<std::vector<int>&>
, get_vector
akan kembali global_vec
dengan referensi, menghasilkan ekspresi nilai yang lebih tinggi.
Jika kita melakukannya:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
Kami mendapatkan output berikut, seperti yang diharapkan:
2
1
2
2
Jika Anda adalah untuk mengubah auto&&
dalam kode untuk setiap auto
, auto&
, const auto&
, atau const auto&&
maka kita tidak akan mendapatkan hasil yang kita inginkan.
Cara alternatif untuk mengubah logika program berdasarkan pada apakah auto&&
referensi Anda diinisialisasi dengan ekspresi lvalue atau rvalue adalah dengan menggunakan ciri-ciri tipe:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
auto&&
? Saya telah berpikir untuk melihat mengapa sebuah range-based untuk loop diperluas untuk digunakanauto&&
sebagai contoh, tetapi belum berhasil. Mungkin siapa pun yang menjawab dapat menjelaskannya.