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

Callback argument type does not match in inherited class

本文关键字:参数 类型 不匹配 回调 继承      更新时间:2023-10-16

我试图实现基于链接代码的事件管理器在这里的顶部答案:游戏对象之间的对话

然而,当我试图注册回调时,我得到了一个错误。我确信它与类型定义有关,我承认我不确定它是如何工作的,但它在链接代码中是完全相同的形式。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*)}’
谁能解释一下我做错了什么?由于

正如@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
    }
};

我认为你最好使用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->echo将调用子

如果需要性能,则使用

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

并且要非常小心void*,它们通常被认为是不好的。

在注释中编辑地址点:非纯虚拟

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

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

void*有很多不好的原因。from c++ FAQ

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

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

搜索"void*"

但是基本上void*绕过了c++中所有的类型安全。这是C中的一个hack,以弥补它没有任何多态性或泛型代码的事实。