Apakah mungkin untuk melewatkan fungsi lambda sebagai penunjuk fungsi? Jika demikian, saya pasti melakukan sesuatu yang salah karena saya mendapatkan kesalahan kompilasi.
Perhatikan contoh berikut
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Ketika saya mencoba mengkompilasi ini , saya mendapatkan kesalahan kompilasi berikut:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
Itu salah satu heck dari pesan kesalahan untuk dicerna, tapi saya pikir apa yang saya dapatkan adalah bahwa lambda tidak dapat diperlakukan sebagai constexpr
jadi karena itu saya tidak bisa meneruskannya sebagai penunjuk fungsi? Saya sudah mencoba membuat x
const juga, tapi itu sepertinya tidak membantu.
c++
c++11
lambda
function-pointers
Cory Kramer
sumber
sumber
Jawaban:
Sebuah lambda hanya dapat dikonversi ke fungsi pointer jika tidak menangkap, dari rancangan C ++ 11 standar bagian
5.1.2
[expr.prim.lambda] mengatakan ( penekanan ):Catatan, cppreferensi juga membahas hal ini di bagian mereka tentang fungsi Lambda .
Jadi, alternatif berikut bisa digunakan:
dan begitu juga ini:
dan seperti yang ditunjukkan 5gon12eder , Anda juga dapat menggunakan
std::function
, tetapi perhatikan bahwa itustd::function
adalah beban yang berat , jadi itu bukan trade-off yang lebih murah.sumber
void*
sebagai parameter tunggal. Biasanya disebut "pointer pengguna". Ini relatif ringan juga, tetapi cenderung mengharuskan Andamalloc
keluar beberapa ruang.Jawaban Shafik Yaghmour dengan tepat menjelaskan mengapa lambda tidak dapat dilewatkan sebagai penunjuk fungsi jika memiliki tangkapan. Saya ingin menunjukkan dua perbaikan sederhana untuk masalah ini.
Gunakan
std::function
sebagai ganti pointer fungsi mentah.Ini adalah solusi yang sangat bersih. Namun perlu dicatat bahwa itu termasuk beberapa overhead tambahan untuk penghapusan tipe (mungkin panggilan fungsi virtual).
Gunakan ekspresi lambda yang tidak menangkap apa pun.
Karena predikat Anda benar-benar hanya konstanta boolean, berikut ini akan dengan cepat mengatasi masalah saat ini. Lihat jawaban ini untuk penjelasan yang baik mengapa dan bagaimana ini bekerja.
sumber
glutReshapeFunc
.std::function
atau lambda - mengapa tidak? Paling tidak itu adalah sintaks yang lebih mudah dibaca. Biasanya kita perlu menggunakan pointer fungsi untuk berinteraksi dengan pustaka C (sebenarnya, dengan pustaka eksternal apa pun) , dan pastikan Anda tidak dapat memodifikasinya untuk menerima std :: function atau lambda.Ekspresi Lambda, bahkan yang ditangkap, dapat ditangani sebagai penunjuk fungsi (penunjuk ke fungsi anggota).
Ini rumit karena ekspresi lambda bukanlah fungsi yang sederhana. Ini sebenarnya adalah objek dengan operator ().
Ketika Anda kreatif, Anda dapat menggunakan ini! Pikirkan kelas "function" dengan gaya std :: function. Jika Anda menyimpan objek, Anda juga dapat menggunakan penunjuk fungsi.
Untuk menggunakan pointer fungsi, Anda dapat menggunakan yang berikut:
Untuk membangun kelas yang dapat mulai bekerja seperti "std :: function", pertama-tama Anda membutuhkan kelas / struct daripada yang dapat menyimpan objek dan fungsi pointer. Anda juga memerlukan operator () untuk menjalankannya:
Dengan ini, Anda sekarang dapat menjalankan lambdas yang ditangkap dan tidak ditangkap, seperti halnya Anda menggunakan yang asli:
Kode ini bekerja dengan VS2015
Pembaruan 04.07.17:
sumber
operator()
implementasi, jadi jika saya membacanya dengan benar, saya pikir itu tidak akan berhasil untuk memanggil lambda menggunakan pointer fungsi C-style , bukan? Itulah pertanyaan awal yang ditanyakan.Menangkap lambda tidak dapat dikonversi menjadi pointer fungsi, seperti yang ditunjukkan oleh jawaban ini .
Namun, seringkali cukup merepotkan untuk menyediakan pointer fungsi ke API yang hanya menerima satu. Metode yang paling sering dikutip untuk melakukannya adalah menyediakan fungsi dan memanggil objek statis dengannya.
Ini membosankan. Kami mengambil ide ini lebih jauh dan mengotomatiskan proses menciptakan
wrapper
dan membuat hidup lebih mudah.Dan gunakan sebagai
Hidup
Ini pada dasarnya mendeklarasikan fungsi anonim pada setiap kemunculan
fnptr
.Perhatikan bahwa doa
fnptr
menimpacallable
callable yang diberikan sebelumnya ditulis dari jenis yang sama. Kami memperbaiki ini, pada tingkat tertentu, denganint
parameterN
.sumber
Pintasan untuk menggunakan lambda dengan sebagai pointer fungsi C adalah ini:
Menggunakan Curl sebagai contoh ( info debug keriting )
sumber
+
triknya untuk Anda).Meskipun pendekatan templat cerdas karena berbagai alasan, penting untuk mengingat siklus hidup lambda dan variabel yang ditangkap. Jika segala bentuk penunjuk lambda akan digunakan dan lambda bukan merupakan kelanjutan ke bawah, maka hanya salinan [=] lambda yang harus digunakan. Yaitu, bahkan kemudian, menangkap pointer ke variabel pada stack tidak aman jika masa pakai pointer yang ditangkap (stack relax) lebih pendek dari masa pakai lambda.
Solusi sederhana untuk menangkap lambda sebagai pointer adalah:
misalnya,
new std::function<void()>([=]() -> void {...}
Ingat saja nanti,
delete pLamdba
jadi pastikan Anda tidak membocorkan memori lambda. Rahasia yang harus disadari di sini adalah bahwa lambda dapat menangkap lambdas (tanyakan pada diri Anda bagaimana cara kerjanya) dan juga agar agarstd::function
dapat bekerja secara umum, implementasi lambda perlu mengandung informasi internal yang memadai untuk menyediakan akses ke ukuran data lambda (dan yang ditangkap) ( itulah sebabnya mengapadelete
harus bekerja [menjalankan destruktor dari tipe yang ditangkap]).sumber
new
- std :: function sudah menyimpan lambda di heap AND dan tidak perlu mengingat panggilan delete.Bukan jawaban langsung, tetapi sedikit variasi untuk menggunakan pola templat "functor" untuk menyembunyikan spesifikasi jenis lambda dan menjaga kode tetap bagus dan sederhana.
Saya tidak yakin bagaimana Anda ingin menggunakan kelas putuskan jadi saya harus memperluas kelas dengan fungsi yang menggunakannya. Lihat contoh lengkap di sini: https://godbolt.org/z/jtByqE
Bentuk dasar kelas Anda mungkin terlihat seperti ini:
Di mana Anda melewati tipe fungsi sebagai bagian dari tipe kelas yang digunakan seperti:
Sekali lagi, saya tidak yakin mengapa Anda menangkapnya
x
lebih masuk akal (bagi saya) untuk memiliki parameter yang Anda berikan ke lambda) sehingga Anda dapat menggunakan seperti:Lihat tautan untuk contoh lengkap
sumber
Seperti yang disebutkan oleh yang lain, Anda dapat mengganti fungsi Lambda alih-alih fungsi pointer. Saya menggunakan metode ini di antarmuka C ++ saya ke F77 ODE RKSUITE pemecah.
sumber