我在静态方法中调用模板函数时遇到问题。 在这里我的代码: baseEvent.h
dom-if
和baseEvent.cpp
#ifndef BASE_EVENT_H
#define BASE_EVENT_H
#include <unordered_map>
#include <functional>
class baseEvent
{
public:
template<typename Sender,typename EventArgs>
using pureEvent = std::function<void(Sender, EventArgs)>;
template<typename pureEvent>
using eventMap = std::unordered_map<int, pureEvent>;
template<typename Sender, typename EventArgs>
struct Event
{
int AddEvent(pureEvent<Sender, EventArgs>);
void RemoveEvent(int);
void LaunchEvents(Sender, EventArgs);
private:
eventMap<pureEvent<Sender,EventArgs>> Events;
};
};
#endif
anotherClass.h
#include "baseEvent.h"
template <typename Sender, typename EventArgs>
int baseEvent::Event<Sender, EventArgs>::AddEvent(pureEvent<Sender, EventArgs> pure_event)
{
auto uniqeID = 0;
if (Events.rbegin() != Events.rend())
uniqeID = Events.rbegin()->first + 1;
Events.insert(std::make_pair(uniqeID, pure_event));
return uniqeID;
}
template <typename Sender, typename EventArgs>
void baseEvent::Event<Sender, EventArgs>::RemoveEvent(int uniqeID)
{
auto it = Events.find(uniqeID);
if (it != Events.end())
Events.erase(it);
}
template <typename Sender, typename EventArgs>
void baseEvent::Event<Sender, EventArgs>::LaunchEvents(Sender sender, EventArgs e)
{
if (Events.empty())return;
for (auto& singleEvent : Events)
{
singleEvent(sender, e);
}
}
anotherClass.cpp
#include "baseEvent.h"
class anotherClass
{
public:
typedef baseEvent::pureEvent<nullptr_t, int> ballEvent;
static void Update();
static int AddOnChangeOwnerEvent(ballEvent);
static void RemoveOnChangeOwnerEvent(int);
static void LaunchOnChangeOwnerEvents(int);
private:
static baseEvent::Event<nullptr_t, int> OnChangeOwnerEvents;
};
它给了我这个错误:
错误2错误LNK2019:未解析的外部符号&#34; public:void __thiscall baseEvent :: Event :: LaunchEvents(std :: nullptr_t,int)&#34; (?LaunchEvents @?$ Event @ $$ TH @ baseEvent @@ QAEX $$ TH @ Z)引用于 function&#34; public:static void __cdecl anotherClass :: LaunchOnChangeOwnerEvents(INT)&#34; (?LaunchOnChangeOwnerEvents @ ball @@ SAXH @ Z)E:\ projects \ ProFCEngine \ SoccerAI \ anotherClass.obj SoccerAI
它应该可以正常工作,我做错了什么?
更新
如果我使用模板struct baseEvent :: Event; LaunchOnChangeOwnerEvents将修复,但如果我在我的主要执行此操作,我会再次收到错误。
#include "anotherClass.h"
baseEvent::Event< nullptr_t, int> ball::OnChangeOwnerEvents;
void ball::Update()
{
LaunchOnChangeOwnerEvents(0);
//.
//.
//.
}
inline int anotherClass::AddOnChangeOwnerEvent(ballEvent ball_event)
{
return OnChangeOwnerEvents.AddEvent(ball_event);
}
inline void anotherClass::RemoveOnChangeOwnerEvent(int uniqeID)
{
return OnChangeOwnerEvents.RemoveEvent(uniqeID);
}
inline void anotherClass::LaunchOnChangeOwnerEvents(int event_args)
{
return OnChangeOwnerEvents.LaunchEvents(nullptr, event_args);
}
答案 0 :(得分:1)
使用模板类,您不能只将实现放在源文件中。源文件不知道将调用函数的模板参数,因此它不会生成任何已编译的函数。因此,您会收到一个链接器错误,该函数未解析。
您可以通过将实现移动到标题中来修复此问题,并与类定义内联。
答案 1 :(得分:0)
太长没看过
最简单的解决方案是在baseEvent.cpp
template struct baseEvent::Event<nullptr_t, int>;
<强>答案强>
让我们假设你的主要是这样的:
#include "anotherClass.h"
int main()
{
anotherClass c;
c.Update();
return 0;
}
当我们编译main.cpp
时 g++ -std=c++11 -c main.cpp -o main.o
并检查我们的翻译单位需要/提供的符号:
$nm -gC main.o
0000000000000000 T main
U anotherClass::Update()
main.cpp
需要anotherClass::Update
的实施。
让我们编译anotherClass.cpp
g++ -std=c++11 -c anotherClass.cpp -o anotherClass.o
再次检查我们的翻译单位需要/提供的符号:
$nm -gC anotherClass.o
[...]
0000000000000000 T anotherClass::Update()
U baseEvent::Event<decltype(nullptr), int>::LaunchEvents(decltype(nullptr), int)
[...]
anotherClass.cpp
提供了很多符号,为了清晰起见我删除了它们。正如您在anotherClass.cpp
编译期间所看到的那样,nullptr_t, int
的LaunchEvents未编译,因为编译器没有定义。
当我们检查baseEvent.o
与nm
的内容时,我们会了解到它没有提供任何符号,也不需要任何符号。
nm -gC baseEvent.o
正如我之前所写,问题是anotherClass.cpp
只声明LaunchEvents
而不是定义。
明确的实例化
第一种方法是强制在baseEvent.cpp
:
template struct baseEvent::Event<nullptr_t, int>;
如果我们在此类更改后检查baseEvent.o
,我们会在此翻译单元中看到所需的符号:
$nm -gC baseEvent.o
[...]
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::RemoveEvent(int)
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::LaunchEvents(decltype(nullptr), int)
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::LaunchEvents(baseEvent::Event<decltype(nullptr), int>, int)
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::AddEvent(std::function<void (decltype(nullptr), int)>)
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::AddEvent(std::function<void (baseEvent::Event<decltype(nullptr), int>, int)>)
[...]
将模板定义移至标题
正如@qxz在他/她的回答中所说,你可以将模板实现移到标题中。您可以在课后添加内联函数
#ifndef BASE_EVENT_H
#define BASE_EVENT_H
#include <unordered_map>
#include <functional>
class baseEvent
{
public:
[...]
template<typename Sender, typename EventArgs>
struct Event
{
int AddEvent(pureEvent<Sender, EventArgs>);
void RemoveEvent(int);
void LaunchEvents(Sender, EventArgs);
private:
eventMap<pureEvent<Sender,EventArgs>> Events;
};
};
[...]
template <typename Sender, typename EventArgs>
inline void baseEvent::Event<Sender, EventArgs>::LaunchEvents(Sender sender, EventArgs e)
{
if (Events.empty())return;
for (auto& singleEvent : Events)
{
singleEvent.second(sender, e);
}
}
#endif
或者在类定义
中添加实现class baseEvent
{
public:
[...]
template<typename Sender, typename EventArgs>
struct Event
{
[...]
void LaunchEvents(Sender sender, EventArgs e)
{
if (Events.empty())return;
for (auto& singleEvent : Events)
{
singleEvent.second(sender, e);
}
}
private:
eventMap<pureEvent<Sender,EventArgs>> Events;
};
};
如果您这样做,将在anotherClass.o
:
$ nm -gC anotherClass.o | grep LaunchEvent
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::LaunchEvents(decltype(nullptr), int)
0000000000000000 W baseEvent::Event<decltype(nullptr), int>::LaunchEvents(baseEvent::Event<decltype(nullptr), int>, int)