これは単純な質問ですが、答えは驚くほど複雑です。std::bind1st
簡単な答えは、またはでやろうとしていることを実行できるということですboost::bind
。長い答えは以下の通りです。
コンパイラは、を使用することを提案するのに正しいです&CLoggersInfra::RedundencyManagerCallBack
。まず、RedundencyManagerCallBack
がメンバー関数の場合、関数自体はクラスの特定のインスタンスに属していませんCLoggersInfra
。それはクラス自体に属しています。以前に静的クラス関数を呼び出したことがある場合は、同じSomeClass::SomeMemberFunction
構文を使用していることに気付いたかもしれません。関数自体は、特定のインスタンスではなくクラスに属するという意味で「静的」であるため、同じ構文を使用します。技術的に言えば、関数を直接渡さないため、「&」が必要です。関数はC++では実際のオブジェクトではありません。代わりに、技術的には関数のメモリアドレス、つまり関数の命令がメモリ内で始まる場所へのポインタを渡します。結果は同じですが、あなたは効果的にです」
しかし、それはこの場合の問題の半分にすぎません。私が言ったようにRedundencyManagerCallBack
、関数は特定のインスタンスに「属していません」。しかし、特定のインスタンスを念頭に置いてコールバックとして渡したいようです。これを行う方法を理解するには、メンバー関数が実際に何であるかを理解する必要があります。つまり、追加の非表示パラメーターを持つ通常のクラス内で定義されていない関数です。
例えば:
class A {
public:
A() : data(0) {}
void foo(int addToData) { this->data += addToData; }
int data;
};
...
A an_a_object;
an_a_object.foo(5);
A::foo(&an_a_object, 5); // This is the same as the line above!
std::cout << an_a_object.data; // Prints 10!
いくつのパラメータがA::foo
必要ですか?通常、1と言います。しかし、内部では、fooは実際には2を取ります。A:: fooの定義を見ると、「this」ポインタを意味のあるものにするために、Aの特定のインスタンスが必要です(コンパイラは何を知る必要があります」これは)。通常、「これ」を指定する方法は、構文を使用することですMyObject.MyMemberFunction()
。ただし、これは、のアドレスをMyObject
最初のパラメータとして渡すための単なる構文糖衣です。MyMemberFunction
。同様に、クラス定義内でメンバー関数を宣言する場合、パラメーターリストに「this」を入れませんが、これは入力を節約するための言語設計者からの贈り物にすぎません。代わりに、メンバー関数が静的であることを指定して、追加の「this」パラメーターを自動的に取得することをオプトアウトする必要があります。C ++コンパイラが上記の例をCコードに変換した場合(元のC ++コンパイラは実際にはそのように機能しました)、おそらく次のようになります。
struct A {
int data;
};
void a_init(A* to_init)
{
to_init->data = 0;
}
void a_foo(A* this, int addToData)
{
this->data += addToData;
}
...
A an_a_object;
a_init(0); // Before constructor call was implicit
a_foo(&an_a_object, 5); // Used to be an_a_object.foo(5);
例に戻ると、明らかな問題があります。'Init'は、1つのパラメーターを受け取る関数へのポインターを必要とします。ただし&CLoggersInfra::RedundencyManagerCallBack
、これは2つのパラメーター、つまり通常のパラメーターと秘密の「this」パラメーターを受け取る関数へのポインターです。そのため、まだコンパイラエラーが発生します(補足:Pythonを使用したことがある場合は、この種の混乱により、すべてのメンバー関数に「self」パラメータが必要になります)。
これを処理するための詳細な方法は、必要なインスタンスへのポインターを保持し、パラメーターを受け取る「run」や「execute」(または「()」演算子をオーバーロードする)などのメンバー関数を持つ特別なオブジェクトを作成することです。メンバー関数の場合、保存されたインスタンスのこれらのパラメーターを使用してメンバー関数を呼び出すだけです。ただし、これには、生の関数ポインターではなく、特別なオブジェクトを取得するように「Init」を変更する必要があり、Initは他の誰かのコードのように聞こえます。そして、この問題が発生するたびに特別なクラスを作成すると、コードの膨張につながります。
だから今、最後に、良い解決策、boost::bind
そしてboost::function
、あなたがここで見つけることができるそれぞれのドキュメント:
boost :: bind docs、
boost :: functiondocs
boost::bind
関数とその関数へのパラメーターを取得し、そのパラメーターが所定の位置に「ロック」されている新しい関数を作成できます。したがって、2つの整数を追加する関数がある場合boost::bind
は、パラメーターの1つが5にロックされている新しい関数を作成するために使用できます。この新しい関数は1つの整数パラメーターのみを取り、常に5を追加します。この手法を使用すると、「非表示の」「this」パラメーターを特定のクラスインスタンスにロックインし、必要に応じて1つのパラメーターのみを受け取る新しい関数を生成できます(非表示のパラメーターは常に最初のパラメーターであることに注意してください。そして、通常のパラメータはその後に順番に来ます)。見てboost::bind
例としてドキュメントがあり、メンバー関数での使用についても具体的に説明しています。[std::bind1st][3]
技術的には、同様に使用できると呼ばれる標準関数がありますが、boost::bind
より一般的です。
もちろん、もう1つだけ問題があります。boost::bind
素晴らしいboost::functionが作成されますが、これは技術的にはInitがおそらく望んでいるような生の関数ポインタではありません。ありがたいことに、boostは、ここのStackOverflowに記載されているように、boost::functionをrawポインターに変換する方法を提供します。これをどのように実装するかは、この回答の範囲を超えていますが、それも興味深いものです。
これがばかげて難しいように思われる場合でも心配しないでください。質問はC++の暗いコーナーのいくつかと交差しており、boost::bind
一度習得すると非常に役立ちます。
C ++ 11アップデート:代わりに、boost::bind
「this」をキャプチャするラムダ関数を使用できるようになりました。これは基本的に、コンパイラに同じものを生成させることです。