C ++ 11 range based loop: dapatkan item dengan nilai atau referensi ke const

215

Membaca beberapa contoh loop berdasarkan rentang mereka menyarankan dua cara utama 1 , 2 , 3 , 4

std::vector<MyClass> vec;

for (auto &x : vec)
{
  // x is a reference to an item of vec
  // We can change vec's items by changing x 
}

atau

for (auto x : vec)
{
  // Value of x is copied from an item of vec
  // We can not change vec's items by changing x
}

Baik.

Ketika kita tidak perlu mengganti vecitem, IMO, Contoh menyarankan untuk menggunakan versi kedua (berdasarkan nilai). Mengapa mereka tidak menyarankan sesuatu yang constreferensi (Setidaknya saya belum menemukan saran langsung):

for (auto const &x : vec) // <-- see const keyword
{
  // x is a reference to an const item of vec
  // We can not change vec's items by changing x 
}

Bukankah ini lebih baik? Bukankah itu menghindari salinan yang berlebihan di setiap iterasi saat itu const?

masoud
sumber

Jawaban:

390

Jika Anda tidak ingin mengubah item serta ingin menghindari membuat salinan, maka itu auto const &adalah pilihan yang benar:

for (auto const &x : vec)

Siapa pun yang menyarankan Anda untuk menggunakan auto &itu salah. Abaikan mereka.

Berikut ini rekap:

  • Pilih auto xkapan Anda ingin bekerja dengan salinan.
  • Pilih auto &xkapan Anda ingin bekerja dengan item asli dan dapat memodifikasinya.
  • Pilih auto const &xkapan Anda ingin bekerja dengan item asli dan tidak akan memodifikasinya.
Nawaz
sumber
21
Terima kasih atas jawabannya. Saya kira itu juga harus menunjukkan bahwa const auto &xitu setara dengan pilihan ketiga Anda.
smg
7
@mloskot: Ini setara. (dan apa yang Anda maksud dengan "tetapi itu adalah sintaks yang sama" ? Sintaksinya terlihat berbeda.)
Nawaz
14
Hilang: auto&&ketika Anda tidak ingin membuat salinan yang tidak perlu, dan tidak peduli apakah Anda memodifikasinya atau tidak, dan Anda hanya ingin bekerja.
Yakk - Adam Nevraumont
4
Apakah perbedaan referensi berlaku untuk salinan jika Anda hanya bekerja dengan tipe dasar seperti int / double?
4
@racarate: Saya tidak bisa mengomentari kecepatan keseluruhan , dan saya pikir tidak ada yang bisa, tanpa membuat profil terlebih dahulu. Terus terang, saya tidak akan mendasarkan pilihan saya pada kecepatan, bukan kejelasan kode. Jika saya ingin kekekalan, saya pasti akan menggunakannya const. Namun, apakah itu akan auto const &, atau auto const memiliki sedikit perbedaan. Saya memilih auto const &hanya untuk lebih konsisten.
Nawaz
23

Jika Anda memiliki std::vector<int>atau std::vector<double>, maka tidak apa-apa untuk digunakan auto(dengan salinan nilai) alih-alih const auto&, karena menyalin intatau a doubleadalah murah:

for (auto x : vec)
    ....

Tetapi jika Anda memiliki std::vector<MyClass>, di mana MyClassmemiliki beberapa semantik salinan non-sepele (misalnya std::string, beberapa kelas kustom yang kompleks, dll) maka saya sarankan menggunakan const auto&untuk menghindari deep-copy :

for (const auto & x : vec)
    ....
Mr.C64
sumber
3

Ketika kita tidak perlu mengganti vecitem, Contoh menyarankan untuk menggunakan versi pertama.

Lalu mereka memberikan saran yang salah.

Kenapa mereka tidak menyarankan sesuatu yang menjadi acuan para const

Karena mereka memberikan saran yang salah :-) Apa yang Anda sebutkan itu benar. Jika Anda hanya ingin mengamati suatu objek, tidak perlu membuat salinan, dan tidak perlu memiliki non-const referensi untuk itu.

EDIT:

Saya melihat referensi yang Anda tautkan semua memberikan contoh iterasi pada rentang intnilai atau beberapa tipe data fundamental lainnya. Dalam hal itu, karena menyalin inttidak mahal, membuat salinan pada dasarnya setara dengan (jika tidak lebih efisien daripada) memiliki pengamatan const &.

Namun, ini bukan kasus umum untuk tipe yang ditentukan pengguna. UDT mungkin mahal untuk disalin, dan jika Anda tidak memiliki alasan untuk membuat salinan (seperti memodifikasi objek yang diambil tanpa mengubah yang asli), maka lebih baik menggunakan a const &.

Andy Prowl
sumber
1

Saya akan bertentangan di sini dan mengatakan tidak perlu auto const &dalam rentang berbasis loop. Beri tahu saya jika Anda menganggap fungsi berikut ini konyol (tidak sesuai tujuannya, tetapi dalam cara tertulisnya):

long long SafePop(std::vector<uint32_t>& v)
{
    auto const& cv = v;
    long long n = -1;
    if (!cv.empty())
    {
        n = cv.back();
        v.pop_back();
    }
    return n;
}

Di sini, penulis telah membuat referensi const vuntuk digunakan untuk semua operasi yang tidak memodifikasi v. Ini konyol, menurut pendapat saya, dan argumen yang sama dapat dibuat untuk digunakan auto const &sebagai variabel dalam rentang yang didasarkan untuk loop, bukan hanya auto &.

Benjamin Lindley
sumber
3
@ BenjaminLindley: Jadi saya bisa menyimpulkan bahwa Anda juga akan membantah const_iteratordalam for for loop? Bagaimana Anda memastikan bahwa Anda tidak mengubah item asli dari sebuah wadah ketika Anda mengulanginya?
Nawaz
2
@BenjaminLindley: Mengapa kode Anda tidak dapat dikompilasi tanpa const_iterator? Biarkan saya menebak, wadah yang Anda iterator berakhir, adalah const. Tapi mengapa constharus memulainya? Di suatu tempat Anda menggunakan constuntuk memastikan apa?
Nawaz
8
Keuntungan membuat objek constsama seperti keuntungan menggunakan private/ protecteddi kelas. Ini menghindari kesalahan lebih lanjut.
masoud
2
@BenjaminLindley: Oh, saya mengabaikannya. Maka dalam hal ini, implementasinya juga konyol. Tetapi jika fungsi itu tidak dimodifikasi v, implementasi IMO akan baik-baik saja. Dan jika loop tidak pernah memodifikasi objek yang dilaluinya, sepertinya benar bagi saya untuk memiliki referensi const. Saya mengerti maksud Anda. Milik saya adalah bahwa loop mewakili unit kode cukup banyak seperti fungsinya, dan dalam unit itu tidak ada instruksi yang perlu mengubah nilai. Oleh karena itu, Anda dapat "menandai" seluruh unit sebagai const, seperti yang akan Anda lakukan dengan constfungsi anggota.
Andy Prowl
1
Jadi Anda berpikir const &untuk rentang berbasis konyol, karena Anda menulis contoh imajiner yang tidak relevan dari seorang programmer imajiner yang melakukan sesuatu yang konyol dengan cv- kualifikasi? ... OK kalau begitu
underscore_d