回调参数类型在继承的类中不匹配

时间:2011-12-13 22:08:09

标签: c++ callback arguments inherited

我正在尝试基于最常见答案中的链接代码实现事件管理器: Game Objects Talking To Each Other

但是当我尝试注册回调时,我收到错误。 我确定它与typedef有关,我承认我不确定它是如何工作的,但它在链接代码中的形式完全相同。 B类应该从接口继承,那么为什么类型不同? 我已经将代码浓缩成下面最小的例子。

#include <iostream>

class Interface;
typedef void (Interface::*Callback)(void *data);

class Interface
{
    public:
        void Register    (Callback func);

};

void Interface::Register(Callback func)
{
    std::cout << "Register" << std::endl;
}


class B : public Interface
{
    public:
        B();
        void Echo(void *data);
};

B::B()
{
    Register( (Callback)Echo );
}

void B::Echo(void *data)
{
    std::cout << "Echo" << std::endl;
}


int main()
{
    B b;
    return 0;
}

这是我在g ++ 4.6.1下得到的错误:

test.cpp: In constructor ‘B::B()’:
test.cpp:31:22: error: argument of type ‘void (B::)(void*)’ does not match ‘Callback {aka void (Interface::*)(void*)}’

有谁能解释我做错了什么? 感谢

2 个答案:

答案 0 :(得分:1)

正如@Kerrek正确指出的那样,Echo不是Interface的成员,因此B::Echo不符合Interface::*Callback的条件。但您可以使用模板来实现这一目标,例如:

template <class T> class Interface {
public:
    typedef void (T::*Callback)(void *data);
    void Register(Callback func) {
        std::cout << "Register" << std::endl;
    }
    // ...
};

class B : public Interface<B> {
public:
    B() {
        Register(&B::Echo);
    }
    void Echo(void *data) {
        // Do something
    }
};

答案 1 :(得分:0)

我认为你最好使用std :: function(c ++ 11)或boost :: function(c ++ 03 + boost)

#include <iostream>

class Interface;
typedef void (Interface::*Callback)(void *data);

class Interface
{
    public:
        std::function<void(void*)> register;

            Interface(std::function<void(void*)> register_)
            :    register(register_)   //intializer list
            {}
            virtual ~Interface(){} //put me in
};

void Interface::Register(Callback func)
{
    std::cout << "Register" << std::endl;
}


class B : public Interface
{
    public:
        B();
        void Echo(void *data);
};

B::B()
:   Interface( std::bind(B::Echo, this) )
{}

void B::Echo(void *data)
{
    std::cout << "Echo" << std::endl;
}

虽然为什么你不使用纯虚拟机是我的

class Interface
{
    public:
        virtual void Echo(void*)=0;

};

void B::Echo(void *data) //implements Echo
{
    std::cout << "Echo" << std::endl;
}

call interface-&gt; echo将调用孩子

如果您需要表现,请使用

http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

对虚空非常小心*通常认为它们很糟糕。

在评论中编辑地址点:非纯虚拟

class Interface
{
public:
    virtual ~Interface(){} //put me in
    virtual void echo(void*){}  //if implementation is not extended it will do nothing.
    //others 
};

这不是Java,接口不是语言定义的东西。通过这种方式,您可以选择一个可以选择实现哪个部分的接口,如果回调与您的类无关,那么就不要实现它。

void *因各种原因而不好。来自C ++ FAQ

  

避免void *(将它们保存在低级函数和数据结构中   如果你真的需要它们并提供类型安全的接口,通常   模板,给您的用户)

http://www2.research.att.com/~bs/bs_faq.html

搜索“void *”

但基本上无效*绕过了C ++从中添加的所有类型安全性。 C中的一个黑客可以弥补它没有任何多态性或通用代码这一事实。