Pertanyaan Bisakah saya membuat kelas anonim di C ++ dan menangkap variabel luar seperti di Java?


Di Java, ketika saya membutuhkan fungsi panggilan balik, saya harus mengimplementasikan kelas anonim. Di dalam kelas anonim, saya dapat mengakses variabel luar jika mereka final.

Sekarang saya melakukan hal yang sama di C ++. Saya mengerti bahwa lambda C ++ bekerja lebih baik tetapi kadang-kadang saya harus lulus dalam banyak fungsi di mana dengan kelas anonim, saya hanya perlu lulus dalam satu contoh.

Saya mencoba contoh berikut. Ia bekerja dengan GCC 4.3.4.

class IA {
public:
  virtual int f(int x) = 0;  
};

int main() {
    class : public IA {
        int f(int x) { return x + 1; }
    } a;
    doFancyWork(&a);
    return 0;
}

Apakah mungkin untuk menangkap variabel luar seperti ini?

int main() {
    int y = 100; // mark y as final if possible
    class : public IA {
        int f(int x) { return x + y; }
    } a;
    return 0;
}

MEMPERBARUI:

Contoh kedua tidak akan dikompilasi. Kesalahannya ada di sini,

prog.cpp: In member function ‘virtual int main()::<anonymous class>::f(int)’:
prog.cpp:9: error: use of ‘auto’ variable from containing function
prog.cpp:7: error:   ‘int y’ declared here
prog.cpp: In function ‘int main()’:
prog.cpp:7: warning: unused variable ‘y’

MEMPERBARUI:

Saya baru saja menyadari beberapa masalah lagi dalam melakukan ini:

  • Saya tidak dapat menulis konstruktor karena kelas tidak memiliki nama
  • daftar penginisialisasi tidak mengizinkan warisan.
  • perubahan apa pun untuk membuatnya terkompilasi membuat kode tidak dapat dibaca.

Saya pikir saya harus pindah dari kelas anonim.


32
2018-01-16 21:57


asal


Jawaban:


Tidak ada cara untuk melakukannya secara otomatis menangkap variabel-variabel tersebut, tetapi Anda dapat menggunakan pendekatan alternatif. Ini jika Anda ingin menangkap dengan referensi:

int main() {
    int y = 100; // mark y as final if possible
    class IB : public IA {
    public:
      IB(int& y) : _y(y) {}
      int f(int x) { return x + _y; }
    private:
      int& _y;
    } a (y);
    return 0;
}

Jika Anda ingin menangkap berdasarkan nilai, cukup ubah int& ke int.

Bagaimanapun, Anda dapat mempertimbangkan untuk menggunakan tupel lambdas sebagai objek "multi-panggilan balik" jika itu yang mengganggu Anda tentang lambdas individu. Anda masih akan memiliki semuanya dikemas dalam satu objek dan menangkap akan dilakukan secara gratis.

Hanya sebagai contoh:

auto callbacks = make_tuple(
    [] (int x) { cout << x << endl; },
    [&] () { cout << y << endl; }, // y is captured by reference
    [=] (int x) { cout << x + y << endl; }, // y is captured by value
    // other lambdas here, if you want...
    );

33
2018-01-16 22:14



Anda dapat menangkap variabel secara manual (yang mirip dengan apa yang dilakukan tangkapan lambda di belakang layar):

int main() {
    int y = 100;
    struct { 
        int& y;
        int operator()(int x) { return x + y; }
    } anon = { y };
}

Anda kemudian dapat menggunakannya seperti ini:

#include <iostream>
...
std::cout << anon(10) << std::endl;

Mencetak 110 seperti yang diharapkan. Sayangnya, Anda tidak dapat memiliki jenis anonim yang mewarisi dari yang lain dengan metode ini sebagai tipe yang dapat di-inisialisasi pembuat daftar tidak dapat mewarisi dari jenis lain. Jika warisan sangat penting maka Anda harus menggunakan metode konstruktor yang digariskan oleh Andy Prowl.


6
2018-01-16 22:11



Lambda C ++ dapat menangkap variabel "luar". [Sunting: ketika saya pertama kali membaca pertanyaan, saya entah bagaimana melewatkan di mana dia menyebutkan bahwa dia menyadari lambdas. Untuk lebih baik atau lebih buruk, C ++ tidak memiliki hal lain yang benar-benar menyerupai kelas anonim].

Sebagai contoh:

#include <iostream>

int main(){ 

    int y = 100;
    auto lambda = [=](int x) { return x + y; };

    std::cout << lambda(2);
}

... cetak 102 sebagai outputnya.

Perhatikan bahwa meskipun terlihat seperti sebuah fungsi, lambda C ++ benar-benar menghasilkan kelas. Saya kira saya harus menambahkan: kelas itu tidak secara teknis anonim, tetapi memiliki beberapa nama yang tidak ditentukan yang tidak pernah secara langsung terlihat.

Edit: Saya masih agak bingung tentang pembenaran untuk tidak menggunakan lambdas sekalipun. Apakah maksud untuk menggunakan satu kelas yang berisi banyak fungsi anggota? Jika demikian, tidak jelas bagaimana Anda berencana untuk menentukan fungsi anggota mana yang akan dipanggil pada waktu mana / untuk tujuan apa. Reaksi langsung saya adalah bahwa ini terdengar mencurigakan seolah-olah Anda mencoba memutar bahasa untuk mendukung desain yang bermasalah.


4
2018-01-16 22:01



Bukan anonimitas kelas yang membatasi akses ke variabel luar. Dalam pertanyaan, y tidak dapat diakses karena kelas didefinisikan secara lokal dalam suatu fungsi.

Ada beberapa batasan untuk kelas yang ditentukan secara lokal. Pertama, mereka hanya dapat mengakses variabel lokal yang statis, tetapi dapat mengakses variabel lain yang tersedia untuk ruang lingkup fungsi. Juga, kelas lokal tidak dapat memiliki anggota data statis.

Adapun kelas anonim, Anda tidak dapat memiliki konstruktor atau destruktor. Semua fungsi anggota harus dideklarasikan di dalam definisi kelas. Tidak dapat memiliki anggota statis statis, ini termasuk anggota statis statis const yang biasanya dapat dipakai dalam definisi kelas. Juga warisan tidak diizinkan.

Kelas anonim adalah sudut C ++ yang tidak jelas dan memiliki nilai praktis yang kecil. Fungsi Lambda dan teknik lainnya jauh lebih fleksibel. Tapi siapa tahu, mungkin dalam beberapa situasi itu bisa membantu dengan keterbacaan kode.


2
2017-12-15 16:12



Jika Anda IA kelas benar-benar hanya memiliki satu metode virtual yang perlu Anda timpa (dan kompleksitas sebenarnya adalah metode non-virtual lainnya) tetapi Anda tidak ingin menangkap variabel lokal bahwa metode ini membutuhkan, bagaimana dengan ini:

int main() {
  int y = 100;
  auto f = [=](int x){return x+y;};
  typedef decltype(f) F;
  struct IB : IA {
    F _f;
    IB(F _f): _f(_f) {}
    int f(int x) { return _f(x); }
  } a(f);
  doFancyWork(&a);
  return 0;
}

1
2018-01-16 22:28