2011-10-08 55 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(); –