Kembali dari fungsi pemanggilan di dalam lambda

Lambdas adalah cara luar biasa untuk membuat kode yang dapat digunakan kembali di dalam suatu fungsi/metode tanpa mencemari kelas induk. Mereka merupakan pengganti yang sangat fungsional untuk makro gaya C di sebagian besar waktu.

Namun, ada sedikit gula sintaksis dari makro yang sepertinya tidak bisa saya tiru dengan lambda, dan itu adalah kemampuan untuk keluar dari fungsi yang memuatnya. Misalnya, jika saya perlu kembali sambil memeriksa rentang rangkaian ints, saya dapat melakukannya dengan mudah menggunakan makro:

const int xmin(1), xmax(5);
#define CHECK_RANGE(x) { if((x) < xmin || (x) > xmax) return false; }

bool myFunc(int myint) {
    CHECK_RANGE(myint);
    int anotherint = myint + 2;
    CHECK_RANGE(anotherint);
    return true;
}

Jelas ini adalah contoh yang terlalu disederhanakan, tetapi premis dasarnya adalah saya melakukan pemeriksaan yang sama berulang kali pada variabel yang berbeda, dan menurut saya lebih mudah dibaca untuk merangkum pemeriksaan dan pintu keluar terkait. Namun, saya tahu bahwa makro tidak terlalu aman, terutama ketika makro tersebut menjadi sangat rumit . Namun, sejauh yang saya tahu, mencoba melakukan lambda yang setara memerlukan pemeriksaan tambahan yang canggung seperti:

const int xmin(1), xmax(5);
auto check_range = [&](int x) -> bool { return !(x < xmin || x > xmax); };

bool myFunc(int myint) {
    if(!check_range(myint)) return false;
    int anotherint = myint + 2;
    if(!check_range(anotherint)) return false;
    return true;
}

Apakah ada cara untuk melakukan ini dengan lambda? Atau apakah saya melewatkan solusi alternatif?

Edit: Saya menyadari bahwa kembali dari dalam makro umumnya merupakan ide yang buruk kecuali tindakan pencegahan yang signifikan telah dilakukan. Saya hanya ingin tahu apakah itu mungkin.


person Phlucious    schedule 31.07.2017    source sumber
comment
ada sedikit gula sintaksis - ya, untuk membuat kodenya tidak dapat dipahami.   -  person    schedule 01.08.2017
comment
Tidak, ini tidak mungkin. Akan lebih baik jika ada cara untuk melakukan hal ini, namun saat ini belum ada   -  person Justin    schedule 01.08.2017
comment
Saya akan memihak Justin sebelum berpikir terlalu banyak tentang bagaimana std::longjmp bisa membantu.   -  person Quentin    schedule 01.08.2017
comment
@Quentin Saya lebih suka makro daripada longjmp. Namun sebenarnya, satu-satunya hal buruk tentang makro adalah ia tidak memeriksa tipe dan sintaksis. makro menghasilkan kode sumber setelah proses kompiler Anda, TIDAK ada rasa tidak aman jika makro digunakan dengan benar. Ada kendala dalam cara Anda menulis makro, mis. parameter digunakan kembali dengan operator tambahan.. atau tidak adanya orang tua di sekitar ekspresi tersebut.   -  person Swift - Friday Pie    schedule 01.08.2017
comment
Anda dapat mencoba mengaturnya dengan pengecualian, yang bergantung pada kasus penggunaan mungkin lebih disukai daripada longjmp.   -  person Daniel H    schedule 01.08.2017
comment
Namun, ada sedikit gula sintaksis... -- untungnya itu cara mengeja yang lucu   -  person rlbond    schedule 01.08.2017
comment
@ manni66 Mungkin dalam beberapa situasi, tetapi dalam kasus ini tampaknya jauh lebih jelas dalam situasi ini (bagi saya) untuk menggunakan makro. Contoh lainnya adalah memeriksa status iostream setelah setiap pembacaan. Tampaknya jauh lebih mudah dibaca bagi saya untuk merangkum pembacaan dengan pemeriksaan status, terutama ketika perubahan selanjutnya memerlukan modifikasi setiap pemeriksaan status.   -  person Phlucious    schedule 01.08.2017
comment
Makro bernama CHECK_RANGE yang memasukkan pengembalian ke dalam kode jelas tidak lebih bersih.   -  person    schedule 01.08.2017
comment
@Swift ya, itu saran yang agak basa-basi sehingga orang yang tahu std::setjmp bisa menakut-nakuti diri mereka sendiri -- Saya lebih suka menggunakan makro juga;)   -  person Quentin    schedule 01.08.2017


Jawaban (4)


Anda benar--tidak ada cara untuk kembali dari pemanggil dari dalam lambda. Karena lambda dapat ditangkap dan disimpan untuk dipanggil nanti, dari dalam pemanggil sewenang-wenang, hal ini akan menghasilkan perilaku yang tidak dapat diprediksi.

class Foo
{
    Foo(std::function<void(int)> const& callMeLater) : func(callMeLater) {}
    void CallIt(int* arr, int count)
    {
        for (index = count; index--;)
            func(count);
        // do other stuff here.
    }
    std::function<void(int)> func;
};

int main()
{
    auto find3 = [](int arr) 
    {
        if (arr == 3)
            return_from_caller; // making up syntax here.
    };

    Foo foo(find3);
};
person Drew Carlson    schedule 31.07.2017

Apakah ada cara untuk melakukan ini dengan lambda?

Tidak persis seperti makro tetapi lambda Anda, alih-alih mengembalikan bool, dapat throw pengecualian khusus (tipe bool, sebagai contoh)

auto check_range
   = [](int x) { if ( (x < xmin) || (x > xmax) ) throw bool{false}; };

dan fungsi myFunc() dapat mencegat tipe khusus ini

bool myFunc (int myint)
 {
   try
    {
      check_range(myint);
      int anotherint = myint + 2;
      check_range(anotherint);
      return true;
    }
   catch ( bool e )
    { return e; }
 }

Untuk satu panggilan check_range(), ini (menurut saya) adalah ide yang buruk; jika Anda memiliki banyak panggilan, saya rasa itu bisa menarik.

Berikut ini adalah contoh kerja lengkap

#include <iostream>

constexpr int xmin{1}, xmax{5};

auto check_range
   = [](int x) { if ( (x < xmin) || (x > xmax) ) throw bool{false}; };

bool myFunc (int myint)
 {
   try
    {
      check_range(myint);
      int anotherint = myint + 2;
      check_range(anotherint);
      return true;
    }
   catch ( bool e )
    { return e; }
 }

int main ()
 {
   std::cout << myFunc(0) << std::endl; // print 0
   std::cout << myFunc(3) << std::endl; // print 1
   std::cout << myFunc(7) << std::endl; // print 0
 }
person max66    schedule 01.08.2017
comment
Daripada membuang bool, biasanya lebih disukai (dan lebih mudah dibaca) untuk membuang struct/class khusus, terutama menjalankan yang berasal dari std::exception (seperti std::out_of_range atau std::range_error), lalu menangkapnya dan menggunakan return false secara langsung. - person Remy Lebeau; 01.08.2017
comment
Memberi +1 karena ini adalah solusi yang saya cari, tetapi jawaban yang diterima menjawab pertanyaan itu sendiri dengan lebih baik. - person Phlucious; 01.08.2017

Tidak ada cara yang lebih baik untuk melakukan ini selain hanya menggunakan nilai kembalian lambda dan kemudian return dari fungsi pemanggil. Makro bagus untuk ini.

Seperti yang ada di C++, itulah cara idiomatis untuk keluar dari suatu fungsi yang menggunakan kondisi lain untuk menentukan apakah akan keluar atau tidak.

person Curious    schedule 31.07.2017

Bukan C++11, tetapi orang-orang telah meretas coroutine C++2a untuk melakukan hal ini.

Ini akan terlihat seperti:

co_await check_range(foo);

dimana kata kunci co_await menunjukkan bahwa dalam beberapa kasus, coroutine ini dapat kembali lebih awal dengan hasil yang tidak lengkap. Dalam kasus Anda, hasil yang tidak lengkap ini akan menjadi kesalahan yang tidak dapat dilanjutkan.

Permainan yang saya lihat adalah dengan opsional, dan diharuskan menggunakan ptr bersama, tetapi segalanya mungkin membaik sebelum distandarisasi.

person Yakk - Adam Nevraumont    schedule 31.07.2017