【问题标题】:c++ error LNK1120: 1 unresolved externals on calling inside of static functionc++ error LNK1120: 1 unresolved externals on call inside of static function
【发布时间】:2016-12-03 10:31:48
【问题描述】:

我在调用静态方法中的模板函数时遇到问题。 这是我的代码: 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

和baseEvent.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 anotherClass::LaunchOnChangeOwnerEvents(int)" (?LaunchOnChangeOwnerEvents@ball@@SAXH@Z) E:\projects\ProFCEngine\SoccerAI\anotherClass.obj SoccerAI

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

更新

如果我使用模板 struct baseEvent::Event; LaunchOnChangeOwnerEvents 会修复,但如果我在主程序中这样做,我会再次出错。

 #include "anotherClass.h"

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

         return 0;
  }

【问题讨论】:

  • 是不是同样的错误?

标签: c++


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

太久没看

最简单的解决方案是在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.cpp 编译期间看到的那样,nullptr_t, int 的 LaunchEvents 没有被编译,因为编译器没有它的定义。

当我们检查 baseEvent.onm 时,我们会发现它没有提供任何符号,也不需要任何符号。

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)

【讨论】:

  • 我收到一个新错误更新了问题你能再帮忙吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
  • 2014-04-22
  • 1970-01-01
  • 2015-04-14
  • 2011-02-09
  • 1970-01-01
  • 2017-03-30
相关资源
最近更新 更多