43

Javaでは、コールバック関数が必要な場合、匿名クラスを実装する必要があります。匿名クラス内では、外部変数にアクセスできますfinal

今、私はC++で同じことをしています。C ++ラムダの方がうまく機能することは理解していますが、匿名クラスでは1つのインスタンスでのみ渡す必要がある多くの関数を渡す必要がある場合があります。

次の例を試してみました。GCC4.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;
}

このような外部変数をキャプチャすることは可能ですか?

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

アップデート:

2番目の例はコンパイルされません。エラーはここにあります、

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’

アップデート:

これを行う際に、さらにいくつかの問題に気づきました。

  • クラスに名前がないため、コンストラクターを記述できません
  • 初期化子リストは継承を許可しません。
  • コンパイルするために変更を加えると、コードが読み取れなくなります。

私は匿名のクラスから離れなければならないと思います。

4

5 に答える 5

38

これらの変数を自動的にキャプチャする方法はありませんが、別の方法を使用できます。これは、参照によってキャプチャする場合です。

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;
}

値でキャプチャする場合は、に変更int&するだけintです。

とにかく、個々のラムダについて気になる場合は、ラムダのタプルを「マルチコールバック」オブジェクトとして使用することを検討してください。すべてが 1 つのオブジェクトにまとめられ、キャプチャは無料で行われます。

例として:

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...
    );
于 2013-01-16T22:14:12.890 に答える
9

変数を手動でキャプチャできます(これは、ラムダキャプチャが舞台裏で行うことと似ています)。

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

その後、次のように使用できます。

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

期待どおりに110を出力します。残念ながら、初期化子リストの構築可能な型は別の型から継承できないため、このメソッドを使用して匿名型を別の型から継承させることはできません。継承が重要な場合は、AndyProwlによって概説されているコンストラクターメソッドを使用する必要があります。

于 2013-01-16T22:11:53.113 に答える
5

C ++ラムダは、「外部」変数をキャプチャできます。[編集:私が最初に質問を読んだとき、彼がラムダを知っていると彼が言ったところをどういうわけか見逃しました。良くも悪くも、C++には匿名クラスに本当に似ているものは他にありません]。

例えば:

#include <iostream>

int main(){ 

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

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

...出力102として出力します。

関数のように見えますが、C++ラムダは実際にはクラスを作成することになります。追加する必要があると思います。そのクラスは技術的に匿名ではありませんが、直接表示されることのない不特定の名前があります。

編集:ラムダを使用しないことの正当性については、まだ少し戸惑っています。多くのメンバー関数を含む1つのクラスを使用する意図はありますか?その場合、どのメンバー関数をいつ、どの目的で呼び出すかをどのように指定するかは明確ではありません。私の即時の反応は、問題のあるデザインをサポートするために言語をひねろうとしているように、これは疑わしいように聞こえるということです。

于 2013-01-16T22:01:45.253 に答える
2

外部変数へのアクセスを制限するのは、クラスの匿名性ではありません。質問では、クラスが関数内でローカルに定義されているため、y にアクセスできません。

ローカルで定義されたクラスにはいくつかの制限があります。まず、静的なローカル変数にしかアクセスできませんが、関数のスコープで使用できる他の変数にはアクセスできます。また、ローカル クラスは静的データ メンバーを持つことはできません。

匿名クラスに関しては、コンストラクタもデストラクタも持つことができません。すべてのメンバー関数は、クラス定義内で宣言する必要があります。静的静的メンバーを持つことはできません。これには、通常、クラス定義内でインスタンス化できる const 静的整数メンバーが含まれます。また、継承は許可されていません。

匿名クラスは、C++ のあいまいなコーナーであり、実用的な価値はほとんどありません。Lambda 関数やその他の手法は、はるかに柔軟です。しかし、場合によってはコードの可読性が向上する可能性があります。

于 2015-12-15T16:12:53.650 に答える
1

クラスにオーバーライドする必要がある仮想メソッドが 1 つしかない場合(そして実際の複雑さは他の非仮想メソッドです)、このメソッドが必要IAとするローカル変数を取得したくない場合は、次のようにします。

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;
}
于 2013-01-16T22:28:12.200 に答える