我正在从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);
}
}
#pragma once
#include "Event.h"
class HandlerFunctionBase
{
public:
virtual ~HandlerFunctionBase() {};
void exec(const Event* event) {call(event);}
private:
virtual void call(const Event*) = 0;
};
#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(__ thishisall LogHandler :: *)(类LogEvent *))”(?? $ RegisterEventFunc @ VLogHandler @@ VLogEvent @@@ EventHandler @@ QAEXPAVLogHandler @@ P81 @ AEXPAVLogEvent @@@ Z @ Z)在函数“public:__thiscall LogHandler :: LogHandler(class EventHandler *)”中引用(?? 0LogHandler @@ QAE @ PAVEventHandler @@@ Z) D:\ Dropbox \ C ++ \ D-Tris \ D-Tris \ D-Tris \ LogHandler.obj D-Tris
RegisterEventFunc是如何解决的?它已经明确实施了!?
答案 0 :(得分:1)
编译器必须知道用于实例化模板以实际生成代码的类型。但它也为每个翻译单元(一个.cpp文件)独立生成代码。它不会“忽略文件”。
所以当你有EventHandler::RegisterEventFunc
的定义时,如果它在这个翻译单元之外被使用(实例化),它就无法知道它将被实例化的参数。
在LogHandler
中,它知道模板EventHandler::RegisterEventFunc
(来自头文件),但由于没有定义,它只是假定在其他地方实例化RegisterEventFunc<LogHandler, LogEvent>
并且不发出任何定义错误。
当它链接在一起时,链接器会发现没有人实际实例化RegisterEventFunc<LogHandler, LogEvent>
,因此它无法将它们链接在一起并发出您看到的错误。
你可以做的是:
1)将EventHandler::RegisterEventFunc
的定义移至EventHandler.h
。 (恕我直言,这是通常的解决方案)
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.
)。甚至不尝试,我为了完整起见而添加它。
您可能感兴趣的一些相关问题:
How do I explicitly instantiate a template function?
Using export keyword with templates
Why can templates only be implemented in the header file?(这实际上看似重复......)
编辑:对于您的其他问题:您无法通过const
从变量中移除static_cast
限定符。抛弃常量是有潜在危险的,应该避免,但如果你绝对需要它,你可以通过const_cast< EventT* >(event)
来完成。