2011-10-08 54 views
7

我有這個問題困擾着我。我有FSM類關聯鍵回調作爲模板參數的問題

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 aFSM; 
// OK 
aFSM.addCallback("one", &FSM::callback< CallbackBase, &CallbackBase::operator() >); 
// KO 
aFSM.addCallback("two", &FSM::callback< Callback, &Callback::operator() >); 

第一個電話是好的,在第二個編譯器抱怨:

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

我也遇到了一些問題,使用基類函數(或運算符)和派生類的成員函數指針。在MSVC atleast中,它只是在模板中不起作用。 – Xeo

回答

2

您還沒有定義Callback :: operator()。 Callback沒有secound功能,只是CallbackBase中的函數,它使用CallbackBase和int作爲參數!這就是編譯器呻吟「無法解析的重載函數類型」的原因。

繼承函數的類型是bool(CallbackBase :: * operator())(int)。這個函數可以自動轉換爲一個bool(Callback :: * operator())(int),因爲你總是可以將一個回調函數應用到一個只接受一個CallbackBase的函數。這就是爲什麼以下的原因 - 這是在那裏發生的自動投射。

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) 

這是因爲通過Callback_t和函數指針所需要的類型給出的類型不起作用instanciating回調時不匹配功能。 您可以在扣除類型之前,通過顯式轉換函數指針(Callback :: * operator())(int)來解決問題。 如果將回調函數更改爲以下內容,則不要求這兩種類型相同,並且在沒有轉換的情況下進行編譯。

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()存在的點。它存在,因爲它是從基類繼承的,事實上我在問題中發佈的最後三行代碼正常工作。事實上,在我看來,更多的是編譯器錯誤。是的,這是真的我可以刪除功能點作爲模板參數,但在這種情況下,我可以只處理函子對象,而事實上,我希望更靈活,允許用戶指定也是成員函數的情況下,他更喜歡有一個單一類將所有回調函數實現爲成員函數。 ... – Simone

+0

..關於爲什麼我定義了operator()和虛擬doCall。這是一種稱爲模板方法的模式。是的,在上面的例子中它是沒用的,但你可以想象在operator()中的CallbackBase中實現了一些在每個派生類中實現的回調函數調用之前和之後執行的預操作和後操作。一個例子可以獲取有關doCall執行時間的統計信息。 – Simone

+0

@simone我不相信編譯器的bug。我試圖在gcc和visual studio上編譯你的代碼,得到相同的結果。在Visual Studio中,錯誤消息是:無法從'bool(__thiscall CallbackBase :: *)(int)'轉換爲'bool(__thiscall Callback :: * const)(int)'。沒有函數Callback :: operator()它只是調用基類中的一個。以下在任何提到的編譯器中都不編譯:typedef void(Callback :: * func)(int); func f =&Callback :: operator(); –