2011-10-08 6 views
7

私はこの問題を抱えています。最初の呼び出しがで、結構ですテンプレートパラメータの問題として機能する

FSM aFSM; 
// OK 
aFSM.addCallback("one", &FSM::callback< CallbackBase, &CallbackBase::operator() >); 
// KO 
aFSM.addCallback("two", &FSM::callback< Callback, &Callback::operator() >); 

:私は、私がメインにあれば仲間のコールバック

class FSM 
{ 
public: 

typedef bool (FSM::*InCallback_t)(int); 
typedef std::map< std::string, InCallback_t > Table; 

// Since I would like to allow the user to register both functors and class member functions 
template< typename Callback_t, bool (Callback_t::*CallbackFunct_t)(int) > 
bool callback(int x) 
{ 
    return (Callback_t().*CallbackFunct_t)(x); 
} 

void addCallback(const std::string& iKey, InCallback_t iCallback) 
{ 
    _table.insert(std::make_pair(iKey, iCallback)); 
} 

    [ ... ] 

private: 
    Table _table; 
}; 

そして、いくつかのコールバッククラス今

class CallbackBase 
{ 
public: 

    bool operator()(int x){ return doCall(x); } 

private: 
    virtual bool doCall(int x){ return true; } 
}; 


class Callback: public CallbackBase 
{ 
private: 
    bool doCall(int x) 
    { 
     std::cout << "Callback\n"; 
     return true; 
    } 
}; 

への鍵FSMクラスを持っています2番目のコンパイラが文句を言う:

Test.cpp: In function ‘int main(int, char**)’: 
Test.cpp:104:77: error: no matching function for call to ‘FSM::addCallback(const char [4], <unresolved overloaded function type>)’ 
Test.cpp:104:77: note: candidate is: 
Test.cpp:24:7: note: void FSM::addCallback(const string&, FSM::InCallback_t) 
Test.cpp:24:7: note: no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘FSM::InCallback_t’ 

はまた、次のように任意のアイデア

typedef bool (Callback::*Function_t)(int); 
Function_t aFunction = &Callback::operator(); 
(Callback().*aFunction)(5); 

罰金であることに気づきますか? ご協力いただきありがとうございます。

Simone

+2

コンパイラのバグのようです。 :| – Nawaz

+0

はい私はそれも考えました。それは非常に奇妙です – Simone

+0

私はそれにもいくつかの問題を抱えていました。派生クラスのmemberfunction-pointerでベースクラス関数(または演算子)を使用しました。少なくともMSVCでは、テンプレートでは機能しません。 – Xeo

答えて

2

あなたはCallback :: operator()を定義していません。コールバックのためのsecound関数はありません。CallbackBaseの関数は、CallbackBaseとintをパラメータとして取ります。これは、コンパイラが「未解決のオーバーロードされた関数型」について浮き彫りになる理由です。

継承された関数の型はbool(CallbackBase :: * operator())(int)です。 CallbackBaseだけを受け入れる関数に常にコールバックを適用できるので、この関数はbool(Callback :: * operator())(int)に自動的に変換できます。これが、以下の作品が登場する理由です。そこでは自動的にキャストが行われます。

typedef bool (Callback::*Function_t)(int); 
Function_t aFunction = &Callback::operator(); 

問題は、テンプレート型推論で起こる:

template< typename Callback_t, bool (Callback_t::*CallbackFunct_t)(int) > 
with: Callback_t = Callback, CallbackFunct_t = bool (CallbackBase::*CallbackFunct_t)(int) 

コールバックをinstanciatingときCallback_tと関数ポインタによって必要なタイプを経由して与えられた型が一致しないので、これは動作しません。関数。 タイプ控除が行われる前に、(Callback :: * operator())(int)への関数ポインタの明示的なキャストで問題を解決できます。 コールバック関数を次のように変更すると、2つの型が同一でなくても、キャストなしでコンパイルされる必要があります。

template< typename Callback_t> 
bool callback(int x) 
{ 
    return Callback_t()(x); 
} 

私が理解できないのは、仮想関数を追加する理由です。次のことは同じではなく、よりシンプルで読みやすく、より高速に(仮想関数呼び出しなし)できますか? doCall関数は公開される必要があります。

template< typename Callback_t> 
bool callback(int x) 
{ 
    return Callback_t().doCall(x); 
} 

また、コールバック関数を静的にすることもできます。 doCall関数が静的であれば、さらに簡単にコールバック関数を廃止し、一時的にdoCallを呼び出さないようにします。

+0

ありがとうございました。いくつかのコメント。 Callback :: operator()が存在する場合のポイントについて。それは基本クラスから継承されているので、実際に質問の中に投稿したコードの最後の3行が正しく動作するため存在します。実際、それは私にはコンパイラのバグが多いようです。はい、それは本当ですテンプレートパラメータとして関数のポイントを削除することができますが、私はより柔軟にしたいと思いますが、私は彼が1つの単一のすべてのコールバックをメンバ関数として実装するクラス。 ... – Simone

+0

..なぜ私はoperator()とvirtual doCallを定義しましたか?これはテンプレートメソッドと呼ばれるパターンです。上記の例では無意味ですが、operator()のCallbackBaseには、各派生クラスで実装されたコールバック関数の呼び出しの前と後に実行されるいくつかの前処理と後処理が実装されていると想像できます。 doCallの実行時間に関する統計を取る例があります。 – Simone

+0

@simone私はコンパイラのバグを信じていません。私はあなたのコードをgcc上でコンパイルしようとしましたが、同じ結果を持つVisual Studio上でコンパイルしようとしました。 Visual Studioのエラーメッセージは、 'bool(__thiscall CallbackBase :: *)(int)'から 'bool(__thiscall Callback :: * const)(int)'に変換できません。 Callback :: operator()は、基本クラスから呼び出す関数ではありません。以下は、上記コンパイラのいずれでもコンパイルされません:typedef void(Callback :: * func)(int); func f =&コールバック::演算子(); –