2013-11-22 89 views
0

我目前正在從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; 
}; 

LogHandler

(我自己的類,首先嚐試使用系統)

#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:無法解析的外部符號 「市民:無效__thiscall事件處理程序:: RegisterEventFunc(類LogHandler *,無效(__thiscall LogHandler :: *)(類的LogEvent *))」(?? $ @ RegisterEventFunc @@ VLogHandler @@ VLogEvent (函數「public:__thiscall LogHandler :: LogHandler(class EventHandler *)」(?? 0LogHandler @@ QAE @ PAVEventHandler @@@ Z)中引用的@ EventHandler @@ QAEXPAVLogHandler @@ P81 @ AEXPAVLogEvent @@@ Z @ Z)D: \ Dropbox \ C++ \ D-Tris \ D-Tris \ D-Tris \ LogHandler.obj D-Tris

RegisterEventFunc是如何解決的?它明確實施!?

+2

您需要將模板化方法的實現放入頭文件中。 – v154c1

+0

您的模板函數定義在.cpp文件中嗎?如果是這樣,您需要讓它們可以訪問實例化模板的代碼。在實踐中,最簡單的方法是將它們放在頭文件中。 – juanchopanza

+0

我做什麼?爲什麼?我認爲C++忽略文件,只是將所有東西拼接在一起! – pixartist

回答

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?(這實際上似乎是重複的....)

編輯:爲了您的其他問題:您不能通過static_cast從變量中刪除const限定符。強制轉換是潛在的危險,應該避免,但如果你絕對需要它,你可以通過const_cast< EventT* >(event)來完成。