Contoh singkat:
#include <iostream>
int main()
{
int n;
[&](){n = 10;}(); // OK
[=]() mutable {n = 20;}(); // OK
// [=](){n = 10;}(); // Error: a by-value capture cannot be modified in a non-mutable lambda
std::cout << n << "\n"; // "10"
}
Pertanyaannya: Mengapa kita perlu mutable
kata kunci? Ini sangat berbeda dari melewati parameter tradisional ke fungsi bernama. Apa alasan di baliknya?
Saya mendapat kesan bahwa seluruh poin dari capture-by-value adalah untuk memungkinkan pengguna untuk mengubah sementara - jika tidak, saya hampir selalu lebih baik menggunakan capture-by-reference, bukan?
Adakah pencerahan?
(Omong-omong, saya menggunakan MSVC2010. AFAIK ini seharusnya standar)
const
secara default!const
secara default.Jawaban:
Ini membutuhkan
mutable
karena secara default, objek fungsi harus menghasilkan hasil yang sama setiap kali dipanggil. Ini adalah perbedaan antara fungsi berorientasi objek dan fungsi menggunakan variabel global, secara efektif.sumber
void f(const std::function<int(int)> g)
. Bagaimana saya dijamin bahwag
sebenarnya transparan referensial ?g
Pemasok mungkin sudah digunakanmutable
. Jadi saya tidak akan tahu. Di sisi lain, jika default adalah nonconst
, dan orang-orang harus menambahkanconst
bukannyamutable
ke objek fungsi, kompilator dapat benar-benar menegakkanconst std::function<int(int)>
bagian dan sekarangf
dapat berasumsi bahwag
adalahconst
, tidak ada?Kode Anda hampir setara dengan ini:
Jadi Anda bisa menganggap lambdas sebagai menghasilkan kelas dengan operator () yang default ke const kecuali Anda mengatakan bahwa itu bisa berubah.
Anda juga dapat menganggap semua variabel yang ditangkap di dalam [] (secara eksplisit atau implisit) sebagai anggota kelas itu: salinan objek untuk [=] atau referensi ke objek untuk [&]. Mereka diinisialisasi ketika Anda menyatakan lambda Anda seolah-olah ada konstruktor tersembunyi.
sumber
const
ataumutable
lambda jika diimplementasikan sebagai jenis yang didefinisikan pengguna yang setara, pertanyaannya adalah (seperti dalam judul dan diuraikan oleh OP dalam komentar) mengapaconst
default, jadi ini tidak menjawabnya.Pertanyaannya adalah, apakah "hampir"? Kasus penggunaan yang sering muncul adalah untuk mengembalikan atau memberikan lambdas:
Saya pikir itu
mutable
bukan kasus "hampir". Saya menganggap "tangkapan-menurut-nilai" seperti "izinkan saya menggunakan nilainya setelah entitas yang ditangkap mati" daripada "izinkan saya mengubah salinannya". Tapi mungkin ini bisa diperdebatkan.sumber
const
? Apa tujuannya?mutable
tampaknya keluar dari tempat di sini, ketikaconst
adalah tidak default di "hampir" (: P) segala sesuatu yang lain dari bahasa.const
adalah default, setidaknya orang akan dipaksa untuk mempertimbangkan const-correctness: /const
sehingga mereka bisa menyebutnya apakah objek lambda adalah const. Misalnya mereka bisa meneruskannya ke fungsi mengambil astd::function<void()> const&
. Untuk memungkinkan lambda mengubah salinan yang diambil, dalam makalah awal, anggota data penutupan didefinisikanmutable
secara internal secara otomatis. Sekarang Anda harus secara manual memasukkanmutable
ekspresi lambda. Saya belum menemukan alasan terperinci.FWIW, Herb Sutter, seorang anggota terkenal dari komite standardisasi C ++, memberikan jawaban yang berbeda untuk pertanyaan itu dalam Masalah Lambda Correctness and Usability :
Makalahnya adalah tentang mengapa ini harus diubah dalam C ++ 14. Ini singkat, ditulis dengan baik, layak dibaca jika Anda ingin tahu "apa yang ada di pikiran [anggota komite]" sehubungan dengan fitur khusus ini.
sumber
Anda perlu memikirkan apa jenis penutupan fungsi Lambda Anda. Setiap kali Anda mendeklarasikan ekspresi Lambda, kompiler membuat tipe penutupan, yang tidak lain adalah deklarasi kelas tanpa nama dengan atribut ( lingkungan di mana ekspresi Lambda di mana dinyatakan) dan pemanggilan fungsi
::operator()
diimplementasikan. Saat Anda menangkap variabel menggunakan nilai salin , kompiler akan membuatconst
atribut baru di tipe closure, jadi Anda tidak bisa mengubahnya di dalam ekspresi Lambda karena itu adalah atribut "read-only", itulah alasan mereka menyebutnya " penutupan ", karena dalam beberapa hal, Anda menutup ekspresi Lambda Anda dengan menyalin variabel dari cakupan atas ke dalam cakupan Lambda.mutable
, entitas yang ditangkap akan menjadinon-const
atribut tipe penutupan Anda. Inilah yang menyebabkan perubahan yang dilakukan dalam variabel yang dapat diubah yang ditangkap oleh nilai, untuk tidak disebarluaskan ke ruang lingkup atas, tetapi tetap di dalam stateful Lambda. Selalu mencoba membayangkan jenis penutupan yang dihasilkan dari ekspresi Lambda Anda, yang banyak membantu saya, dan saya harap itu dapat membantu Anda juga.sumber
Lihat draf ini , di bawah 5.1.2 [expr.prim.lambda], subclause 5:
Sunting pada komentar litb: Mungkin mereka memikirkan capture-by-value sehingga perubahan luar pada variabel tidak tercermin di dalam lambda? Referensi bekerja dua arah, jadi itulah penjelasan saya. Tidak tahu apakah itu ada gunanya.
Sunting pada komentar kizzx2: Yang paling sering digunakan lambda adalah sebagai functor untuk algoritma.
const
Ness default memungkinkannya digunakan dalam lingkungan yang konstan, sama seperticonst
fungsi normal-wajar dapat digunakan di sana, tetapi yang non-const
wajar tidak bisa. Mungkin mereka hanya berpikir untuk membuatnya lebih intuitif untuk kasus-kasus itu, yang tahu apa yang terjadi dalam pikiran mereka. :)sumber
const
default? Saya sudah mendapatkan salinan baru, sepertinya aneh untuk tidak membiarkan saya mengubahnya - terutama itu bukan sesuatu yang salah dengan itu - mereka hanya ingin saya tambahkanmutable
.var
kata kunci untuk memungkinkan perubahan dan konstan menjadi default untuk yang lainnya. Sekarang tidak, jadi kita harus hidup dengan itu. IMO, C ++ 2011 keluar dengan cukup baik, mengingat semuanya.n
adalah tidak temporer. n adalah anggota objek fungsi lambda yang Anda buat dengan ekspresi lambda. Harapan standarnya adalah bahwa memanggil lambda Anda tidak mengubah keadaannya, oleh karena itu ia mencegah Anda memodifikasi secara tidak sengajan
.sumber
Anda harus memahami apa arti penangkapan! itu menangkap bukan lewat argumen! mari kita lihat beberapa contoh kode:
Seperti yang Anda lihat meskipun
x
telah diubah ke20
lambda masih kembali 10 (x
masih5
di dalam lambda) Mengubahx
di dalam lambda berarti mengubah lambda itu sendiri pada setiap panggilan (lambda bermutasi pada setiap panggilan). Untuk menegakkan kebenaran, standar memperkenalkanmutable
kata kunci. Dengan menentukan lambda sebagai bisa berubah, Anda mengatakan bahwa setiap panggilan ke lambda dapat menyebabkan perubahan dalam lambda itu sendiri. Coba lihat contoh lain:Contoh di atas menunjukkan bahwa dengan membuat lambda bisa berubah, mengubah
x
di dalam lambda "mengubah" lambda pada setiap panggilan dengan nilai barux
yang tidak ada hubungannya dengan nilai aktualx
dalam fungsi utamasumber
Sekarang ada proposal untuk mengurangi kebutuhan
mutable
dalam deklarasi lambda: n3424sumber
mutable
itu bahkan kata kunci dalam C ++.Untuk memperluas jawaban Puppy, fungsi lambda dimaksudkan sebagai fungsi murni . Itu berarti setiap panggilan yang diberikan set input unik selalu mengembalikan output yang sama. Mari kita tentukan input sebagai himpunan semua argumen plus semua variabel yang ditangkap saat lambda dipanggil.
Dalam fungsi murni, output semata-mata tergantung pada input dan bukan pada beberapa kondisi internal. Karena itu fungsi lambda, jika murni, tidak perlu mengubah kondisinya dan karenanya tidak dapat diubah.
Ketika lambda menangkap dengan referensi, menulis pada variabel yang ditangkap adalah tekanan pada konsep fungsi murni, karena semua fungsi murni harus dilakukan adalah mengembalikan output, meskipun lambda tidak pasti bermutasi karena penulisan terjadi pada variabel eksternal. Bahkan dalam kasus ini penggunaan yang benar menyiratkan bahwa jika lambda dipanggil dengan input yang sama lagi, output akan sama setiap kali, meskipun ada efek samping pada variabel by-ref. Efek samping semacam itu hanyalah cara untuk mengembalikan beberapa input tambahan (mis. Memperbarui penghitung) dan dapat diformulasi ulang menjadi fungsi murni, misalnya mengembalikan tupel alih-alih nilai tunggal.
sumber