テンプレートへのオーバーロードされたメンバー関数ポインター
-
11-09-2019 - |
質問
次のようなテンプレートでメンバー関数ポインターを保存しようとしています。(これは実際のコードの簡略化されたバージョンです)
template<class Arg1>
void connect(void (T::*f)(Arg1))
{
//Do some stuff
}
template<class Arg1>
void connect(void (T::*f)())
{
//Do some stuff
}
class GApp
{
public:
void foo() {}
void foo(double d) {}
};
次に、GApp のオーバーロードされたすべてのメソッドに対して次のようにしたいと思います。
connect(&GApp::foo);
これを呼び出すのは foo()
それはOKですが、これをどのように呼び出すことができますか foo(double d)
?以下が機能しないのはなぜですか?
connect((&GApp::foo)(double));
それは私に与えます
構文エラー :「double」の前に「)」を付ける必要があります
ここで使用する必要がある構文がわかりません。これは愚かな質問かもしれませんが、誰かこれについて助けてくれませんか?
解決
書かれたコードはコンパイルされません。あなたがやりたいことについていくつかの「仮定」を立てて、コードを変更しました。
要約すると、関数パラメーターの型を明示的に指定することで、正しい関数を呼び出すことができます。
connect<double> (&GApp::foo);
接続メソッドがクラス テンプレートのメンバーである場合、クラス タイプを指定する必要があるのは 1 回だけです。
template <typename T> class A
{
public:
template<class Arg1>
void connect(void (T::*f)(Arg1))
{
//Do some stuff
}
void connect(void (T::*f)())
{
//Do some stuff
}
};
class GApp
{
public:
void foo() {}
void foo(double d) {}
};
int main ()
{
A<GApp> a;
a.connect (&GApp::foo); // foo ()
a.connect<double> (&GApp::foo); // foo (double)
}
アップデート:
新しいコード サンプルに応答して、すべての情報が渡されます。「まれな」ケースは「signal_void」ケースで、シグナルにはテンプレート引数があるが、メンバー関数にはテンプレート引数がありません。したがって、その例を特殊なケースに設定して完了です。以下がコンパイルされるようになりました。
template <class Arg = void>
class signal {};
signal<double> signal_double;
signal<> signal_void;
// Arg1 is deduced from signal<Arg1> and then we use it in the declaration
// of the pointer to member function
template<class T, class Arg1>
void connect ( signal<Arg1>& sig, T& obj, void (T::*f)(Arg1) ) {}
// Add special case for 'void' without any arguments
template<class T>
void connect (signal<> & sig, T& obj, void (T::*f)()) {}
void bar ()
{
GApp myGApp;
//Connecting foo()
connect(signal_void, myGApp, &GApp::foo); // Matches second overload
//Connecting foo(double)
connect(signal_double, myGApp, &GApp::foo); // Matches first overload
}
他のヒント
C ++プログラミング言語、3E、7.7節、P159ます:
あなたはに割り当てまたは関数へのポインタを初期化することにより、オーバーロード関数のアドレスを取ることができます。その場合には、ターゲットのタイプは、オーバーロードされた関数の集合から選択するために使用されます。たとえばます:
void f(int);
int f(char);
void (*pf1)(int) = &f; // void f(int);
int (*pf2)(char) = &f; // int f(char);
void (*pf3)(char) = &f; // error: no void f(char)
私の知る限り(チェックしていない)知っているように、同じことがメンバ関数に適用されます。だから、解決策は、2行に分割することが考えられます:
connect((&GApp::foo)(double));
となります:
void (GApp::*tmp)(double) = &GApp::foo;
connect(tmp);
変数tmp
を呼び出すことはありません; - )
私はnewacctのキャストがまったく同じ理由で、あまりにも安全であることを推測します。 void (GApp::*)(double)
にキャストする型void (GApp::*)(double)
の一時的に初期化と同じになるように定義されます。それを初期化するために使用される式が&GApp::foo
ですので、私はオーバーロードされた機能を持つ他の初期化に適用されるのと同じ魔法をキャストに適用することを期待します。 Stroustrup氏は、「関数へのポインタ変数を初期化する」と言っていない、彼は「関数へのポインタを初期化する」と言います。だから一時を含める必要があります。
あなたはワンライナーを好むので、もします:
connect((void (GApp::*)(double))(&GApp::foo));
しかし、私は私がそうであるように、標準的な一貫性の同じ考えを持っている、と私はチェックしていないと仮定しています。
あなたは、それはこのように、選択するかを知っているように、ポインタをキャスト明示的に試すことができます:
connect((void (GApp::*)(double))&GApp::foo);
免責事項:それをテストしていません。
これは働いている、
typedef void (GApp::*MemberFunctionType)(double);
MemberFunctionType pointer = &GApp::foo;
connect(MemberFunctionType);
それはなぜですか?
EDIT
うーん...ええ。それはnewacctの溶液と同じです。誰もがPLSのソリューションを与えることができますか?
ブースト::関数ライブラリを使用して...
#include <boost/function.hpp>
template<class Arg1>
void connect(boost::function1<void, Arg1*> fn)
{
//Do some stuff
}
template<class Arg1>
void connect(boost::function2<void, Arg1*, double> fn)
{
//Do some stuff
}
class GApp
{
public:
void foo() {}
void foo(double d) {}
};
int main()
{
boost::function1<void,GApp*> f1 = (void (GApp::*)(void)) &GApp::foo;
boost::function2<void,GApp*,double> f2 = (void (GApp::*)(double)) &GApp:foo;
connect(f1);
connect(f2);
return 0;
}
私のオリジナルコードは次のようである、
コネクタ....
template<class T, class Arg1>
void connect(signal<Arg1>& sig,T& obj, void (T::*f)())
{
// sig.insert(new GFunction<T, Arg1>(&obj,f));
}
template<class T, class Arg1
void connect(signal<Arg1>& sig,T& obj, void (T::*f)(Arg1))
{
// sig.insert(new GFunction<T, Arg1>(&obj,f));
}
信号...
signal<double> signal_double;
signal<> signal_void;
アプリケーション...
class GApp
{
public:
void foo() {}
void foo(double d) {}
};
Finaly、接続...
//Connecting foo()
connect(signal_void, myGApp, &GApp::foo()); //Ok
//Connecting foo(double)
connect(signal_double, myGApp, &GApp::foo()); // but ERROR!
(ここで言及されていない)信号用のテンプレートclasssがあります。私は、今では状況がより明確であると思います。 (またはpreviouseと同じことです?)。 (fooが存在しない場合には、第2の接続)が動作します()のみのfoo(ダブル)。それは私のコードは私を傷つけるものです。 :(