C++函数不接受具体实现

Fab*_*ian 6 c++ templates abstract-class

我正在尝试实现类型安全的事件总线.我坚持使用该EventBus::subscribe函数,因为它不接受我的具体事件处理程序.在早期版本中,我只将其AbstractEventHandler实现为抽象类,而不是模板.我对该实现没有任何问题.这就是为什么我认为实际问题是抽象模板.

下面的代码是我的实现的精简版本.第一个块由事件总线的"骨架"及其所需的类组成,而第二个块显示事件,事件处理程序和主要的实际实现.

enum拥有各种不同的事件可用.抽象事件是所有具体事件的基础.事件处理程序是一个抽象模板,其中事件作为模板类以确保类型安全.事件总线负责将所有已发布的事件分发给其各自的处理程序.

enum EVENT_TYPE 
{
    ON_EVENT_1,
    ON_EVENT_2
};

class AbstractEvent 
{
public:
    AbstractEvent() {};
    virtual ~AbstractEvent() {};

    virtual EVENT_TYPE type() = 0;
};

template<class T>
class AbstractEventHandler 
{
public:
    AbstractEventHandler() {};
    virtual ~AbstractEventHandler() {};

    virtual void on_event(T *event) = 0;
};

class EventBus 
{
public:
    EventBus() {};
    virtual ~EventBus() {};

    void subscribe(EVENT_TYPE type, 
                    AbstractEventHandler<AbstractEvent> *eventHandler) {
        // Add handler to vector for further use
    }

    void publish(AbstractEvent *event) {
        // send event to each handler in respective vector
    }
};
Run Code Online (Sandbox Code Playgroud)

下面是我的具体事件和事件处理程序以及main()

class ConcreteEvent : public AbstractEvent 
{
public:
    ConcreteEvent() {};
    virtual ~ConcreteEvent() {};

    EVENT_TYPE type() {
        return ON_EVENT_1;
    };
};

class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> 
{
public:
    ConcreteEventHandler() {}
    virtual ~ConcreteEventHandler() {};

    void on_event(ConcreteEvent *event) {
        // Do something
    };
};

int main() 
{
    EventBus *eventBus = new EventBus();

    ConcreteEventHandler handler = ConcreteEventHandler();

    // This failes!
    eventBus->subscribe(ON_EVENT_1, &handler);
}
Run Code Online (Sandbox Code Playgroud)

编译器返回错误,指出调用没有匹配函数

EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 
Run Code Online (Sandbox Code Playgroud)

并且唯一的候选人是

void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 
Run Code Online (Sandbox Code Playgroud)

如何实现我的EventBus :: subscribe方法来接受我的抽象类的具体实现?

更新:解决方案

我已将方法描述更改为EventBus::subscribe以下内容,现在它可以正常工作:

template<typename T>
void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) {

}
Run Code Online (Sandbox Code Playgroud)

谢谢,罗汉,你的提示!他们帮我找到了这个解决方案.

Roh*_*bhu 5

原因是因为,ConcreteEventHandler是一个子类AbstractEventHandler<ConcreteEvent>而不是AbstractEventHandler<AbstractEvent>.

这可能看起来令人惊讶,但即使是子类,AbstractEventHandler<ConcreteEvent>也不能成为子类.AbstractEventHandler<AbstractEvent>ConcreteEventAbstractEvent

原因是,使用模板,根据需要进行模板化并不能保证类型安全.我们来看一个例子.让我们在基类的范式标准Animal和子类CatDog.假设我们有一份动物名单:

std::list<Animals>* animals;
Run Code Online (Sandbox Code Playgroud)

和一个猫的名单:

std::list<Cat> cats;
Run Code Online (Sandbox Code Playgroud)

以下,不是有效的演员:

animals = &cats;
Run Code Online (Sandbox Code Playgroud)

原因是,因为,如果我这样做,

animals->add(new Dog("Ben"));
Run Code Online (Sandbox Code Playgroud)

我实际上会添加一个s Dog列表Cat.cats.last()这里实际上会返回一个Dog.因此,在这种情况下,您实际上是添加一个s Dog列表Cat.我已经看过足够的Looney Tunes剧集,知道这不是一个好主意:

cats.last().meow();
Run Code Online (Sandbox Code Playgroud)

以上是绝对不正确的,因为我们都知道,Dog只有能bowbow().

编辑

要回答你的问题,我建议你这样做; 让我们ConcreteEventHandlerAbstractEventHandler<AbstractEvent>代码中继承,无论你在哪里使用a ConcreteEvent,dynamic_case都要使用a 来转换AbstractEvent为a ConcreteEvent.这将使用运行时内省,这可能会影响性能(我也见过很多人反对使用动态转换),但您将能够成功执行数据类型的有效向上转换.