Ada alasan untuk tidak menggunakan lambda global?

89

Kami memiliki fungsi yang menggunakan lambda internal yang tidak menangkap sendiri, misalnya:

void foo() {
  auto bar = [](int a, int b){ return a + b; }

  // code using bar(x,y) a bunch of times
}

Sekarang fungsi yang diimplementasikan oleh lambda menjadi dibutuhkan di tempat lain, jadi saya akan mengangkat lambda keluar dari foo()ruang lingkup global / namespace. Saya bisa membiarkannya sebagai lambda, menjadikannya opsi salin-tempel, atau mengubahnya ke fungsi yang benar:

auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2

void foo() {
  // code using bar(x,y) a bunch of times
}

Mengubahnya ke fungsi yang tepat itu sepele, tapi itu membuat saya bertanya-tanya apakah ada alasan untuk tidak meninggalkannya sebagai lambda? Apakah ada alasan untuk tidak menggunakan lambda di mana saja alih-alih fungsi global "biasa"?

Baruch
sumber
Saya berasumsi menangkap variabel yang tidak diinginkan akan menyebabkan banyak kesalahan jika lambdas tidak digunakan dengan hati-hati
macroland
beberapa argumen untuk youtube.com/watch?v=Ft3zFk7VPrE
sudo rm -rf slash

Jawaban:

61

Ada satu alasan yang sangat penting untuk tidak menggunakan lambda global: karena itu tidak normal.

Sintaks fungsi reguler C ++ telah ada sejak zaman C. Pemrogram telah mengetahui selama beberapa dekade apa arti sintaksis dan bagaimana mereka bekerja (walaupun harus diakui bahwa keseluruhan hal peluruhan fungsi-ke-pointer terkadang menggigit bahkan programmer berpengalaman). Jika seorang programmer C ++ dari tingkat keahlian apa pun di luar "newbie mengucapkan" melihat definisi fungsi, mereka tahu apa yang mereka dapatkan.

Global lambda adalah binatang yang sama sekali berbeda. Ini memiliki perilaku yang berbeda dari fungsi biasa. Lambdas adalah objek, sedangkan fungsi tidak. Mereka memiliki tipe, tetapi tipe itu berbeda dari tipe fungsinya. Dan seterusnya.

Jadi sekarang, Anda telah meningkatkan kemampuan dalam berkomunikasi dengan programmer lain. Seorang programmer C ++ perlu memahami lambdas jika mereka akan mengerti apa fungsi ini lakukan. Dan ya, ini tahun 2019, jadi seorang programmer C ++ yang baik harus memiliki gambaran seperti apa lambda. Tapi itu masih bar yang lebih tinggi.

Dan bahkan jika mereka memahaminya, pertanyaan pada pikiran programmer itu adalah ... mengapa penulis kode ini menulis seperti itu? Dan jika Anda tidak memiliki jawaban yang baik untuk pertanyaan itu (misalnya, karena Anda secara eksplisit ingin melarang kelebihan muatan, seperti dalam poin penyesuaian Ranges), maka Anda harus menggunakan mekanisme umum.

Pilih solusi yang diharapkan daripada yang baru jika sesuai. Gunakan metode yang paling tidak rumit untuk menyampaikan maksud Anda.

Nicol Bolas
sumber
9
"Sejak zaman C" Jauh sebelum itu temanku
Lightness Races in Orbit
4
@LightnessRaces: Poin utama saya adalah untuk menyatakan bahwa sintaks fungsi C ++ sudah ada sejak C, jadi banyak orang tahu apa itu dengan inspeksi.
Nicol Bolas
2
Setuju dengan semua jawaban ini kecuali untuk kalimat pertama. "Itu tidak normal" adalah pernyataan yang kabur dan kabur. Mungkin Anda bersungguh-sungguh dalam arti "tidak biasa dan membingungkan"?
einpoklum
1
@einpoklum: C ++ memiliki banyak hal yang dapat dianggap "tidak umum dan membingungkan" yang masih sangat "normal" dalam domain mereka (lihat metaprogramming templat mendalam). Saya pikir "normal" sangat valid dalam hal ini; itu bukan hal yang ada dalam "norma" pengalaman pemrograman C ++, baik secara historis maupun saat ini.
Nicol Bolas
@NicolBolas: Tapi dalam pengertian itu, ini alasan melingkar: OP bertanya-tanya apakah kita harus mengadopsi praktik yang langka. Praktik langka bukanlah "norma", hampir secara definisi. Tapi nm.
einpoklum
53

Saya dapat memikirkan beberapa alasan Anda ingin menghindari lambda global sebagai pengganti drop-in untuk fungsi reguler:

  • fungsi reguler dapat kelebihan beban; lambdas tidak bisa (ada teknik untuk mensimulasikan ini, namun)
  • Terlepas dari kenyataan bahwa mereka seperti fungsi, bahkan lambda non-menangkap seperti ini akan menempati memori (umumnya 1 byte untuk tidak menangkap).
    • seperti yang ditunjukkan di komentar, kompiler modern akan mengoptimalkan penyimpanan ini jauh di bawah as-jika aturan

"Mengapa saya tidak harus menggunakan lambdas untuk menggantikan functor (kelas) stateful?"

  • kelas hanya memiliki batasan lebih sedikit daripada lambdas dan karenanya harus menjadi hal pertama yang Anda raih
    • (data publik / pribadi, kelebihan beban, metode pembantu, dll.)
  • jika lambda memiliki negara, maka semakin sulit untuk memikirkan kapan ia menjadi global.
    • Kita harus memilih untuk membuat instance kelas pada ruang lingkup sesempit mungkin
  • sudah sulit untuk mengubah lambda yang tidak menangkap menjadi penunjuk fungsi, dan tidak mungkin untuk lambda yang menentukan apa pun dalam tangkapannya.
    • kelas memberi kita cara mudah untuk membuat pointer fungsi, dan mereka juga yang membuat banyak programmer lebih nyaman
  • Lambdas dengan tangkapan apa pun tidak dapat dibangun secara default (dalam C ++ 20. Sebelumnya tidak ada konstruktor default dalam hal apa pun)
AndyG
sumber
Ada juga pointer "ini" implisit ke lambda (bahkan yang tidak menangkap) yang menghasilkan parameter tambahan saat memanggil lambda vs memanggil fungsi.
1201ProgramAlarm
@ 1201ProgramAlarm: Itu poin bagus; itu bisa menjadi jauh lebih sulit untuk mendapatkan pointer fungsi untuk lambda (meskipun lambdas yang tidak menangkap dapat membusuk menjadi pointer fungsi biasa)
AndyG
3
Di sisi lain, jika dilewatkan di suatu tempat alih-alih langsung dipanggil, memiliki lambda alih-alih fungsi mempromosikan inlining. Secara alami, seseorang dapat mengemas fungsi-pointer dalam std::integral_constantuntuk itu ...
Deduplicator
@Deduplicator: " memiliki lambda alih-alih fungsi mempromosikan inlining " Hanya untuk memperjelas, ini hanya berlaku jika "suatu tempat" adalah templat yang mengambil fungsi yang dipanggil sebagai jenis yang dapat dipanggil secara sewenang-wenang, alih-alih sebagai penunjuk fungsi.
Nicol Bolas
2
@AndyG Anda lupa aturan as-if: Program yang tidak meminta ukuran lambda (karena ... mengapa ?!), atau membuat pointer ke sana, tidak perlu (dan umumnya tidak) mengalokasikan ruang untuk itu.
Konrad Rudolph
9

Setelah bertanya, saya memikirkan alasan untuk tidak melakukan ini: Karena ini adalah variabel, mereka rentan terhadap Fiasco Order Inisialisasi Statis ( https://isocpp.org/wiki/faq/ctors#static-init-order ), yang dapat menyebabkan bug di telepon.

Baruch
sumber
Anda bisa membuatnya constexprlambda ... intinya adalah: Cukup gunakan fungsi.
einpoklum
8

Apakah ada alasan untuk tidak menggunakan lambda di mana saja alih-alih fungsi global "biasa"?

Masalah tingkat kompleksitas tertentu memerlukan solusi setidaknya kompleksitas yang sama. Tetapi jika ada solusi yang kurang kompleks untuk masalah yang sama, maka benar-benar tidak ada pembenaran untuk menggunakan yang lebih kompleks. Mengapa memperkenalkan kompleksitas yang tidak Anda butuhkan?

Antara lambda dan fungsi, fungsi hanyalah jenis entitas yang kurang kompleks dari keduanya. Anda tidak perlu membenarkan tidak menggunakan lambda. Anda harus membenarkan menggunakan satu. Ekspresi lambda memperkenalkan tipe penutupan, yang merupakan tipe kelas yang tidak disebutkan namanya dengan semua fungsi anggota khusus yang biasa, operator panggilan fungsi, dan, dalam hal ini, operator konversi implisit untuk penunjuk fungsi, dan membuat objek jenis itu. Copy-menginisialisasi variabel global dari ekspresi lambda hanya melakukan banyak lebih dari sekedar mendefinisikan fungsi. Ini mendefinisikan tipe kelas dengan enam fungsi yang dinyatakan secara implisit, mendefinisikan dua fungsi operator lagi, dan membuat objek. Kompiler harus melakukan lebih banyak. Jika Anda tidak membutuhkan fitur lambda, maka jangan gunakan lambda…

Michael Kenzel
sumber
6

Lambdas adalah fungsi anonim .

Jika Anda menggunakan lambda bernama, itu berarti Anda pada dasarnya menggunakan fungsi anonim bernama. Untuk menghindari oxymoron ini, Anda sebaiknya menggunakan fungsi.

Eric Duminil
sumber
1
Ini tampaknya merupakan argumen dari terminologi, yaitu. penamaan dan makna yang membingungkan. Ini dapat dengan mudah dibantah dengan mendefinisikan bahwa lambda yang bernama tidak lagi anonim (duh). Masalahnya di sini bukan bagaimana lambda digunakan, itu adalah bahwa "fungsi anonim" adalah keliru, dan tidak lagi akurat ketika lambda terikat pada sebuah nama.
Konrad Rudolph
@KonradRudolph: Ini bukan hanya terminologi, itu sebabnya mereka diciptakan di tempat pertama. Paling tidak secara historis, lambdas adalah fungsi anonim, dan itu sebabnya membingungkan untuk menyebutkannya, terutama ketika fungsi akan diharapkan sebagai gantinya.
Eric Duminil
1
Tidak Lambdas secara rutin dinamai (hanya biasanya secara lokal), tidak ada yang membingungkan tentang itu.
Konrad Rudolph
1
Tidak setuju dengan fungsi anonim , ini lebih merupakan functor, tapi bagaimanapun, saya tidak melihat masalah untuk memiliki (bernama) contoh daripada memaksa untuk menggunakannya hanya sebagai rvalue-referensi. (karena poin Anda juga harus berlaku pada lingkup lokal).
Jarod42
5

jika ada alasan untuk tidak meninggalkannya sebagai lambda? Apakah ada alasan untuk tidak menggunakan lambda di mana saja alih-alih fungsi global "biasa"?

Kami dulu menggunakan fungsi alih-alih fungsi global, sehingga merusak koherensi dan Prinsip yang paling tidak mengejutkan .

Perbedaan utama adalah:

  • fungsi bisa kelebihan beban, sedangkan functors tidak bisa.
  • fungsi dapat ditemukan dengan ADL, bukan functors.
Jarod42
sumber