14

質問は厳密には についてstd::functionであり、 ではありませんboost::function。詳細については、この質問の下部にある更新std::functionセクションを参照してください。特に、C++ 11 標準に従って空でないオブジェクトを比較できないことに関する部分です。


C++11std::functionクラス テンプレートは、コールバックのコレクションを維持するのに最適です。vectorたとえば、それらを に保存し、必要なときに呼び出すことができます。ただし、これらのオブジェクトを維持し、登録解除を許可することは不可能のようです。

具体的に言って、このクラスを想像してください:

class Invoker
{
public:
  void Register(std::function<void()> f);
  void Unregister(std::function<void()> f);

  void InvokeAll();

private:
  // Some container that holds the function objects passed to Register()
};

サンプル使用シナリオ:

void foo()
{
}

int main()
{
  std::function<void()> f1{foo};
  std::function<void()> f2{[] {std::cout << "Hello\n";} };

  Invoker inv;

  // The easy part

  // Register callbacks
  inv.Register(f1);
  inv.Register(f2);

  // Invoke them
  inv.InvokeAll();

  // The seemingly impossible part. How can Unregister() be implemented to actually
  // locate the correct object to unregister (i.e., remove from its container)?
  inv.Unregister(f2);
  inv.Unregister(f1);
}

Register()関数を実装する方法はかなり明確です。ただし、実装についてはどうすればよいでしょうかUnregister()。関数オブジェクトを保持するコンテナがvector<std::function<void()>>. Unregister()呼び出しに渡される特定の関数オブジェクトをどのように見つけますか? std::functionはオーバーロードされた を提供しますが、それは空の関数オブジェクトをテストするだけです (つまり、2 つの空でない関数オブジェクトを比較して、両方が同じ実際の呼び出しoperator==を参照しているかどうかを確認するために使用することはできません)。

アイデアをいただければ幸いです。

アップデート:

std::functionこれまでのアイデアは、主に、オブジェクトの登録を解除するために使用できる各オブジェクトに関連付けられる Cookie の追加で構成されています。std::functionオブジェクト自体に外生的ではない何かを望んでいました。std::functionまた、 と の間には多くの混乱があるようboost::functionです。問題は厳密にはオブジェクトに関するstd::functionものであり、オブジェクトではありません boost::function

また、2 つの空でないstd::functionオブジェクトが等しいかどうかを比較することはできません。それらは常に標準に従って等しくないものを比較します。したがって、この質問のコンテキストでは、コメント内のそれを行う (そしてboost::functionオブジェクトを使用して起動する) ソリューションへのリンクは明らかに間違っています。

4

2 に答える 2

13

コンテナー内の要素 ID をテストすることはできないため、コンテナーが変更されたときにイテレーターが無効にならないコンテナー ( などstd::list) を使用し、登録解除に使用できる呼び出し元の登録にイテレーターを戻すことをお勧めします。

vector本当に(または)を使用したいdeque場合は、コールバックが追加されたときに整数インデックスを vector/deque に返すことができます。この戦略では、シーケンス内の関数の位置を識別するために、インデックスがこの方法で使用可能であることを確認する必要があります。コールバックや登録解除がめったにない場合、これは単にスポットを再利用しないことを意味する可能性があります。または、空きリストを実装して空のスロットを再利用することもできます。または、シーケンスの最後から空のスロットのみを再利用し、スロットが最初から再利用されるときに増加するベース インデックス オフセットを維持します。

コールバック アクセス パターンがランダム アクセス トラバーサルを必要としない場合、コールバックを に格納し、std::list生の反復子を使用して登録を解除するのが最も簡単に思えます。

于 2013-04-10T19:47:13.537 に答える