事件处理程序模板:未解析的外部

Event handler template: unresolved external

本文关键字:外部 程序 事件处理      更新时间:2023-10-16

我目前正在从C#过渡到C++,并且不断遇到障碍。我从一个教程中得到了一个事件处理程序系统,并试图将其适应我的需求,但有一个我无法理解的错误:

事件:

#pragma once
class Event
{
protected:
   virtual ~Event() {};
};

事件处理程序:

#pragma once
#include "Event.h"
#include "TypeInfo.h"
#include "HandlerFunctionBase.h"
#include <map>
#include <typeindex>
class EventHandler
{
public:
    void HandleEvent(const Event*);
    template < class T, class EventT >
    void RegisterEventFunc(T*, void (T::*memFn)(EventT*));
private:
    typedef std::map<std::type_index, HandlerFunctionBase* > Handlers;
    Handlers _handlers;
};

[…]

#include "EventHandler.h"
template < class T, class EventT >
void EventHandler::RegisterEventFunc(T* obj, void (T::*memFn)(EventT*))
{
    _handlers[std::type_index(typeid(EventT))]=
    new MemberFunctionHandler< T, EventT >(obj, memFn);
}
void EventHandler::HandleEvent(const Event* event)
{
    Handlers::iterator it = _handlers.find(std::type_index(typeid(*event)));
    if(it != _handlers.end())
    {
        it->second->exec(event);
    }
}

HandlerFunctionBase:

#pragma once
#include "Event.h"
class HandlerFunctionBase
{
public:
  virtual ~HandlerFunctionBase() {};
  void exec(const Event* event) {call(event);}
private:
  virtual void call(const Event*) = 0;
};

MemberFunctionHandler:

#pragma once
#include "handlerfunctionbase.h"
template < class T, class EventT >
class MemberFunctionHandler : public HandlerFunctionBase
{
public:
  typedef void (T::*MemberFunc)(EventT*);
  MemberFunctionHandler(T* instance, MemberFunc memFn) : _instance(instance), _function(memFn) {};
  void call(const Event* event)
  {
    (_instance->*_function)(static_cast< EventT* >(event));
  }
private:
  T* _instance;
  MemberFunc _function;
};

日志处理程序

(我自己的课,首先尝试使用系统)

#pragma once
#include "EventHandler.h"
#include "LogEvent.h"
class LogHandler
{
public:
    LogHandler(EventHandler*);
    ~LogHandler(void);
private:
    void Handle(LogEvent*);
};
#[...]
#include "LogHandler.h"

LogHandler::LogHandler(EventHandler *handler)
{
    //This line causes the error
    handler->RegisterEventFunc<LogHandler, LogEvent>(this, &LogHandler::Handle);
}

LogHandler::~LogHandler(void)
{
}
void LogHandler::Handle(LogEvent* e)
{
}

当我试图编译这个时,我得到了什么:

错误1错误LNK2019:未解析的外部符号"public:void __thiscall EventHandler::RegisterEventFunc(类LogHandler*,void(__thiscallLogHandler::*)(类LogEvent*))"($RegisterEventFunc@VLogHandler@@VLogEvent@@@EventHandler@@QAEXPAVLogHandler@@P81@AEXPAVLogEvent@@@Z@Z)在函数"public:__thiscall LogHandler::LogHandler(类EventHandler*)"中引用(??0LogHandler@@QAE@PAVEventHandler@@@Z) D:\Dropbox\C++\D-Tris \D-Tris \D-Tris\LogHandler.obj D-Tris

RegisterEventFunc是如何解析的?它得到了明确的实施!?

编译器必须知道用于实例化模板的类型才能真正生成代码。但它也为每个翻译单元(一个.cpp文件)独立生成代码。它不会"忽略文件"。

因此,在有了EventHandler::RegisterEventFunc的定义时,如果它在这个翻译单元之外使用(实例化),它不知道将使用哪些参数来实例化它。

LogHandler中,它知道模板EventHandler::RegisterEventFunc(来自头文件),但由于没有定义,它只是假设在其他地方有RegisterEventFunc<LogHandler, LogEvent>的实例化,并且不会发出任何错误。

当它链接在一起时,链接器会发现没有人实际实例化RegisterEventFunc<LogHandler, LogEvent>,因此无法将其链接在一起,并发出您看到的错误。

你能做的是:

1) 将CCD_ 6的定义移动到CCD_。(IMHO,这是通常的解决方案)

2) 或者在EventHandler.cpp中强制显式实例化,如

template
void EventHandler::RegisterEventFunc<LogHandler, LogEvent>
       (LogHandler* obj, void (LogHandler::*memFn)(LogEvent*))

这个"解决方案"打破了封装,增加了许多依赖项,维护起来非常麻烦。

3) 或者使用exported模板。C++通过关键字export支持(支持的)模板的使用方式。它只得到了Comeau和ICC的支持(GCC、CLANG和MSVC都不支持这一点),现在它已从标准中删除(在N3690中,在[diff.cpp03.temp]中(附录C.2.7,第1240页),标准上写着:A valid C++ 2003 declaration containing export is ill-formed in this International Standard.)。甚至不要尝试,我添加它只是为了完整。

您可能感兴趣的一些相关问题:

如何显式实例化模板函数?

使用导出关键字与模板

为什么模板只能在头文件中实现?(这实际上看起来像是重复的……)

编辑:对于您的另一个问题:您不能通过static_cast从变量中删除const限定符。驱散恐慌是潜在的危险,应该避免,但如果你绝对需要,你可以通过const_cast< EventT* >(event)来做到。