Alasan dibalik penghapusan operator tugas lambda?

Dec 13 2020

Saya baru saja mengetahui bahwa mutablelambda (genap ) di C ++ tidak dapat dialihkan kecuali memiliki tangkapan kosong (lih ClosureType::operator=).

Contoh:

auto x = 0;
auto l0 = [copy = x]() mutable {};
auto l1 = []() mutable {};

static_assert(not std::is_copy_assignable_v<decltype(l0)>);
static_assert(std::is_copy_assignable_v<decltype(std::ref(x))>);
static_assert(std::is_copy_assignable_v<decltype(l1)>);

Saya menanyakan alasan di balik pilihan ini: Mengapa operator=dihapus? Khususnya dalam skenario, di mana ia bisa menjadi default yaitu lambda mutabledan semua tangkapan dapat disalin sendiri (misalnya l0dalam contoh di atas)?

Saya mengetahui pertanyaan terkait ini untuk non- mutablelambda. Tapi saya ingin memahami keputusan itu daripada mengatasinya.

Jawaban

3 JeffGarrett Dec 14 2020 at 23:59

Saya menduga itu tidak diusulkan. Lambdas memasuki bahasa yang jauh lebih lemah daripada objek fungsi yang mereka gula dan perlahan-lahan mendapatkan kembali fungsionalitas. Sehubungan dengan fungsi anggota khusus, P0624 mengusulkan penambahan assignability dan default-constructibility untuk lambda yang tidak menangkap. Hanya konstruksi-default yang diusulkan di R0, karena itulah yang dibutuhkan penulis dan bisa dibilang merupakan kekurangan yang paling jelas, tetapi penugasan diusulkan di R1 berdasarkan umpan balik komite.

Konstruksi default untuk lambda dengan tangkapan pasti konsisten dengan bahasa:

auto x1 = [i = 1]() { return i; };
static_assert(not std::is_default_constructible_v<decltype(x1)>); // why??

struct { int i = 1; auto operator()() { return i; } } x2;
static_assert(std::is_default_constructible_v<decltype(x2)>);

Penetapan juga konsisten dan berguna. Sebagai satu contoh yang terlintas dalam pikiran, ada proposal di beberapa titik untuk memiliki analogi std::default_deleteuntuk pengalokasi, yaitu jenis yang dapat digunakan sebagai parameter template untuk std::unique_ptruntuk penunjuk yang dialokasikan pengalokasi. Anda dapat membayangkan menggunakan lambda untuk menangkap pengalokasi dan menggunakannya untuk tujuan seperti itu:

auto allocator_delete(auto& allocator) {
    using traits = typename std::allocator_traits<std::decay_t<decltype(allocator)>>;
    return [alloc=std::ref(allocator)](typename traits::pointer p) { traits::deallocate(alloc, p, 1); };
}
template<class Alloc> using allocator_deleter_t = decltype(allocator_delete(std::declval<Alloc&>()));
static_assert(not std::is_move_assignable_v<std::unique_ptr<int, allocator_deleter_t<std::allocator<int>>>>);
// why??

Tetapi Anda tidak dapat me-rebind (pindah-tugaskan ke) ini unique_ptr, karena lambda menghapus tugas secara artifisial, meskipun status penangkapannya memungkinkan. Tulis ulang ini sebagai tipe objek fungsi dan unique_ptrdapat dialihkan, dengan operator tugas yang dihasilkan untuk tipe objek fungsi.

Itu hanya satu contoh, tapi mudah-mudahan ini menjelaskan bahwa apakah Anda ingin menetapkan atau tidak ke status tangkap (the std::ref(allocator)) tidak sama dengan apa yang boleh dilakukan operator panggilan ke status tangkap. (Jawaban pertanyaan terkait salah.)