Тип аргумента обратного вызова не совпадает в унаследованном классе

Я пытаюсь реализовать менеджер событий на основе связанного кода в верхнем ответе здесь: игровые объекты говорят друг с другом

Однако я получаю сообщение об ошибке при попытке зарегистрировать обратные вызовы. Я уверен, что это связано с 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 ответа

Решение

Как правильно Kerrek SB, 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;
}

интерфейс вызова-> эхо вызовет ребенка

если вам нужна производительность, используйте

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*"

но в основном void* обходит все типы безопасности, которые C++ добавил из своего пути добавления. Это взлом в C, чтобы восполнить тот факт, что он не имеет полиморфизма или общего кода.

Другие вопросы по тегам