C++ 错误 LNK1120:在静态函数内部调用时有 1 个未解析的外部

c++ error LNK1120: 1 unresolved externals on calling inside of static function

本文关键字:外部 LNK1120 错误 静态函数 调用 内部 C++      更新时间:2023-10-16

我在静态方法中调用模板函数时遇到问题。 这是我的代码: baseEvent.h

#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

和基本事件.cpp

#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);
}
}

另一个类.h

#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;
};

另一个类.cpp

#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);
}

它给了我这个错误:

错误

2 错误 LNK2019:未解析的外部符号"public: void __thiscall baseEvent::Event::LaunchEvents(std::nullptr_t,int)" (?LaunchEvents@?$Event@$$TH@baseEvent@@QAEX$$TH@Z) 引用于 功能"公共:静态空隙__cdecl otherClass::LaunchOnChangeOwnerEvents(int)" (?LaunchOnChangeOwnerEvents@ball@@SAXH@Z) E:\projects\ProFCEngine\SoccerAI\otherClass.obj SoccerAI

它应该工作正常,我做错了什么?

更新

如果我使用模板结构库事件::事件;启动更改所有者事件将修复,但如果我在我的主目录中执行此操作,我会再次出现错误。

#include "anotherClass.h"
int main()
{
anotherClass c;
ball::AddOnChangeOwnerEvent([](nullptr_t sender, int args)
{
std::cout << "here";
});
c.Update();
return 0;
}

使用模板类,不能只将实现放在源文件中。源文件不知道将使用哪些模板参数调用函数,因此它不会生成任何已编译的函数。因此,您会收到一个链接器错误,指出函数"未解析"。

您可以通过将实现移动到标头中来解决此问题,与类定义内联。

太久没读

最简单的解决方案是在baseEvent.cpp末尾添加以下行

template struct baseEvent::Event<nullptr_t, int>;

假设您的 main 看起来像这样:

#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.cppLaunchEvents fornullptr_t, int没有编译,因为编译器没有其定义。

当我们检查nmbaseEvent.o时,我们会了解到它没有提供任何符号,也不需要任何符号。

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)