Why am I getting a Linker error with template func

2019-03-03 09:26发布

问题:

I have a class EventMgr which has a template function to register a listener. But, when I register a listener, linker gives me a "error LNK2019: unresolved external symbol".

Appetizer code:

class EventMgr {

 template< class T, class EvenT>
 void RegisterListener(T* listener, int EventType, void (T::*MemFunc)(EvenT*) );
}

SoundMgr(which is a listener) tries to register for event:

SoundMgr::SoundMgr(void)
{
  EventManager::GetInstance()->RegisterListener(this, 1, (&SoundMgr::handleBulletFired));
}

I'm not sure why it won't link. Why can it not find the reference types ?

回答1:

If you simply declared the template in a .h file and the implementation is instead in a .cpp file then this is the error you will get, because a C++ compiler works one compile unit at a time. When the compiler finds your code calling a template function that has been just declared it will assume that the concrete instantiation will be done by some other compilation unit (there is no way the compiler can know where to find the .cpp file for that function... the compiler only sees one .cpp and all the included .h at a time).

If the template parameters are from a well known list you can simply request the all explicit implementations you know that will be needed for the program in the .cpp.

For example if you have a template function

template<typename T>
T foo(T x)
{
   ...
}

and you know are just going to need int foo(int); and string foo(string); then it's fine to use just the declaration in the .h, provided you also add two lines in the .cpp saying:

template<> int foo(int);
template<> string foo(string);

By doing that you are telling the compiler about what specialization to build. If you later end up using other specializations (e.g. vector<int> foo(vector<int>)) then you must also add this explicit instantiation in the .cpp file of the template.

However in your example looking at the code I guess that you don't know in advance which kind of events will be defined and so this explicit instantiation cannot be done.

The other solution is to simply put the whole template implementation in the .h file instead of separating declaration from implementation. This sometimes can be not trivial because requires you to expose more implementation details probably introducing more dependencies.



回答2:

This probably means that RegisterListener is not actually implemented anywhere. As it is a template function, you should implement it in the header.

Try the following in the header.

template< class T, class EvenT > 
void RegisterListener(T* listener, int EventType, void (T::MemFunc)(EvenT) ) { }