web-dev-qa-db-ja.com

ラムダ関数をコールバックとして渡し、保存する

これがコールバックを書くための受け入れられたアプローチになるかどうか私は思っていました:

コールバックの保存:

struct EventHolder {
    std::function<void()> Callback;
    EventTypes::EventType Type;
};
std::vector<Events::EventHolder> EventCallbacks;

メソッド定義:

void On(EventType OnEventType,std::function<void()>&& Callback)
{
    Events::EventHolder NewEvent;
    NewEvent.Callback=std::move(Callback);
    NewEvent.Type=OnEventType;
    EventCallbacks.Push_back(std::move(NewEvent));
}

バインディングイベント:

Button->On(EventType::Click,[]{
    // ... callback body
});

私の最大の質問は、値によってコールバックを渡すことです。これは有効なアプローチですか?

21
Grapes

これは、イベントハンドラーを格納するための完全に有効なアプローチです。

ただし、コールバックを追加するための関数のシグネチャに関するいくつかの詳細を指摘したいと思います。値で渡すか参照で渡すかを心配します。あなたの例では、あなたは現在持っています:

void On(EventType OnEventType,std::function<void()>&& Callback)

これを右辺値にのみバインドする限り、これは良いことです。ただし、許可しない特別な理由がない限り、必要に応じて、値または左辺値参照でパラメーターを受け入れ、右辺値参照バージョンを補足として追加するメソッドを常に使用することをお勧めします。

左辺値参照を受け取るメソッドがないということは、次のような場合、コードは現在コンパイルに失敗することを意味します。

std::function<void()> func([](){/*something clever*/});
// Do something necessary with func, perhaps logging or debug prints.
Button->On(EventType::Click, func);

簡単にするために、値を渡す方法を選択するときはいつでも、一般的に次のガイドラインに従うだけで済みます。

  • 渡された実際のオブジェクトを変更せずに、送信された値のコピーが必要な場合、または変更する場合:値渡し。
  • 送信される値を変更するつもりで、これらの変更が渡される実際のオブジェクトに影響するようにしたい場合:参照渡し。
  • 渡されたオブジェクトを変更したくないが、コピーを避けることが有益だと考えている場合:const参照で渡します。
  • パラメータを値、参照、またはconst参照で取得し、入力パラメータが一時的であるという知識を使用して達成できる価値のある最適化があると考えている場合:右辺値参照による受け渡しも許可します。
14
Agentlien

はい。関数は、生の関数ポインターまたは軽量クラスのいずれかであり、コピーコンストラクターには副作用がないため、コピーは元のオブジェクトとして機能する必要があるため、このアプローチは完全に問題ありません。しかし、オブジェクトを値で渡し、それを元のコンテナーに移動する理由は、参照を渡し、それをコンテナーにコピーして、r値の参照を受け入れるオーバーロードされた関数がある場合です(それほど重要ではありません)。

1
BigBoss