带有模板类的 Windows DLL 链接器错误

Posted

技术标签:

【中文标题】带有模板类的 Windows DLL 链接器错误【英文标题】:Windows DLL linker errors with template class 【发布时间】:2015-01-27 22:12:31 【问题描述】:
template <typename SenderType__, typename... Args__>
class CORE_API Event

public:
  typedef typename std::function<void(const SenderType__*, Args__ ...)> EventHandler;  

  Event& operator+=(const EventHandler& toSubscribe)
  
    Subscribe(toSubscribe);
    return *this;
  

  void operator()(const SenderType__* sender, Args__ ... args) const
  
    Invoke(sender, args...);
  

  void Subscribe(const EventHandler& toSubscribe)
  
    std::lock_guard<std::mutex> locker(m_callbackMutex);
    m_callbacks.push_back(toSubscribe);
  

  void Clear()
  
    std::lock_guard<std::mutex> locker(m_callbackMutex);
    m_callbacks.clear();
  

  void Invoke(const SenderType__* sender, Args__ ... args) const
  
    std::lock_guard<std::mutex> locker(m_callbackMutex);
    for (auto iter = m_callbacks.begin(); iter != m_callbacks.end(); ++iter)
    
      (*iter)(sender, args...);
    
  

private:
  std::vector<EventHandler> m_callbacks;
  mutable std::mutex m_callbackMutex;
;

template class CORE_API Event<std::string, std::string>;

在 DLL 的使用者中......

TEST(EventTest, TestEventFiresAndPassesArgs)

  Event<std::string, std::string> event;
  event += &TestFunction;
  event += &TestFunction2;
  std::string sender = "TestEventFiresAndPassesArgs";
  std::string arg = "boo!";
  event.Invoke(&sender, arg);
  ASSERT_EQ(sender, testEventFiresAndPassesArgsSenderName);
  ASSERT_EQ(arg, testEventFiresAndPassesArgsTestArg);
  ASSERT_EQ(sender + "Function2", testEventFiresAndPassesArgsSenderName2);
  ASSERT_EQ(arg + "Function2", testEventFiresAndPassesArgsTestArg2);

那么链接器的输出是:

EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > & __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::operator+=(class std::function<void __cdecl(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const *,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)> const &)" (__imp_??Y?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEAAAEAV012@AEBV?$function@$$A6AXPEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Z@std@@@Z) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::Invoke(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const *,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)const " (__imp_?Invoke@?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEBAXPEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V45@@Z) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(void)" (__imp_??0?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEAA@XZ) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::~Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(void)" (__imp_??1?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEAA@XZ) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>..\runCoreUnitTests.exe : fatal error LNK1120: 4 unresolved externals

有什么想法我在这里做错了吗?

【问题讨论】:

是头文件还是什么 @Alf 这是一个头文件。这是一个编译成dll的lib。那么消费者就是我的单元测试可执行文件——它是一个 cpp 文件.. 嗯,那么 CORE_API 是什么意思呢?您不能从 DLL 导出模板。 #if defined (_MSC_VER) #pragma warning(disable : 4251) #ifdef Core_EXPORTS #define CORE_API __declspec(dllexport) #else #define CORE_API __declspec(dllimport) #endif /* Core_EXPORTS / #else / 已定义 (_WIN32) */ #define CORE_API #endif @Cheers 和 hth。 - Alf 如果我不能导出模板,那么它们几乎一文不值,而且 MSFT 编译器很糟糕。 【参考方案1】:

创建 DLL 时需要使用__declspec(dllexport) 导出函数。

您可以通过使用 __declspec(dllimport) 声明这些函数来从另一个 DLL 中使用这些函数。

这些对于常规功能非常有用。

但是,对于类模板和函数模板,模板会根据需要进行实例化。它们不会被导出到定义它们的 DLL 中。因此,它们也无法从 DLL 中导入。因此,不要将__declspec(dllexport)__declspec(dllimport) 与类模板和函数模板一起使用。

【讨论】:

这似乎表明您不能使用 DLL 定义的模板。 当您在 DLL 中定义模板时,类/函数不会真正得到定义,直到有人尝试实例化模板。模板就像蓝图。他们在那里构建具体的类和函数。 正在导出一个显式实例化(第一个代码块的最后一行)。这应该是可能的,虽然我自己从未尝试过/cc @JonathanHenson 感谢您对动态库中的导出/导入类/函数模板的简单解释!我在编译 dll 时遇到问题。我从 dll 导出所有类和类模板时出错(我尝试编译 linux dll 并通过查找和替换简单地将 EXPORT 宏添加到所有类)。我有很多与问题无关的错误。然后,我在这里找到了您的帖子,删除了所有带有类模板的 EXPORT 宏,并且错误消失了:)

以上是关于带有模板类的 Windows DLL 链接器错误的主要内容,如果未能解决你的问题,请参考以下文章

模板类的链接器错误

构造函数重载类获取链接器错误使用DLL?

CWinApp dll 文件给出带有 CWinApp 类的概率

MinGW 链接器错误:winsock

使用 yaml-cpp 0.5 DLL 时的链接器错误

链接器链接,但可执行文件要求另一个 dll