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 constreferensi 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 forlingkaran berbasis rentang . Lihat pertanyaan saya yang lain untuk lebih jelasnya.
Jika kemudian Anda gunakan std::forwardpada 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_elsewhereuntuk 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 varnyali diri kita sendiri - itu mungkin nilai yang lebih baik atau bahkan const. Namun kita bisa std::forwardke fungsi lain yang benar-benar dapat merusak bagian dalamnya. Segera setelah kami melakukan ini, kami harus mempertimbangkan vardalam keadaan tidak valid.
Sekarang mari kita terapkan ini pada kasus auto&& var = foo();, seperti yang diberikan dalam pertanyaan Anda, di mana foo mengembalikan Tnilai berdasarkan. Dalam hal ini kita tahu pasti bahwa jenis varakan disimpulkan sebagai T&&. Karena kita tahu pasti bahwa itu adalah nilai, kita tidak perlu std::forwardizin untuk mencuri sumber dayanya. Dalam kasus khusus ini, mengetahui bahwa fookembali 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_lvaluemungkin 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_vectormelalui parameter templat foo.
Ketika kami menelepon foo<std::vector<int>>, get_vectorakan kembali global_vecdengan nilai, yang memberikan ekspresi nilai. Atau, saat kita menelepon foo<std::vector<int>&>, get_vectorakan kembali global_vecdengan 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.