首先,这不是一个重复的问题,因为1)这是一个链接器问题,编译器已经通过了,因为我已经显式实例化了。2)这不是关于模板类,而是模板成员函数。3)我对代码结构有一些限制,所以一些现有的技巧不适用。我在这里搜索了我的标题和前几个线程(40832391,20330521,25320619,12848876,36940394),都是关于模板类的,而不是模板成员函数。其他一些线程实际上是在讨论实例化失败,所以实际上是编译器问题,但我已经尝试过显式实例化并且编译已经成功通过了,再次重申。所以我希望你能稍微抑制一下关闭我的问题的冲动,这就是我的线程。
环境:
- Windows 10 版本 1803
- Visual Studio 2015 更新 3
- 在 VS 中使用 Debug x64 模式
源代码:
有两个项目:
1)DllProject,构建为 dll,包含两个源文件:Dll.h 和 Dll.cpp。
Dll.h:
#pragma once
#ifdef _WINDLL
#define API_TYPE __declspec(dllexport)
#else
#define API_TYPE __declspec(dllimport)
#endif
class API_TYPE AClass {
public:
template <class T> void Func(T& data);
template <class T> void CallFunc(T& data) {
Func<T>(data);
}
};
Dll.cpp:
#include "Dll.h"
template <class T> void AClass::Func(T& data) {
data++;
}
template void AClass::Func<float>(float&); //attempt to explicitly instantiate
2)ExeProject是一个exe文件,包含Exe.cpp。
Exe.cpp:
#include "Dll.h"
int main() {
AClass a;
float f = 0.f;
a.CallFunc<float>(f);
}
正如您所见,我想要的是仅调用dll中定义的模板成员函数CallFunc
,它又调用了另一个核心模板成员函数Func
来完成真正的工作。我不需要在exe中直接调用Func
,因此也不需要在dll中导出它。只有CallFunc
是需要导出的API,并且它能够正常工作。DllProject项目可以正确编译,ExeProject项目也可以无问题编译。但在链接时出现错误:
1>------ Build started: Project: ExeProject, Configuration: Debug x64 ------
1>Exe.obj : error LNK2019: unresolved external symbol "public: void __cdecl AClass::Func<float>(float &)" (??$Func@M@AClass@@QEAAXAEAM@Z) referenced in function "public: void __cdecl AClass::CallFunc<float>(float &)" (??$CallFunc@M@AClass@@QEAAXAEAM@Z)
1>C:\tmp\DllProject\x64\Debug\ExeProject.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
我已经在Dll.cpp的第8行显式实例化了模板成员函数Func,所以编译可以通过。但是,在CallFunc内部无法链接到来自dll中的这个实例化函数。我认为函数是在dll中实例化的,而不是在exe中,因为我为该类指定了__declspec(dllimport)。我不知道为什么CallFunc找不到Func;它就在它的正上方。
这些源代码是从一个大型开源项目中隔离和最小化的(DllProject对应库本身,而ExeProject用于测试代码),因此模板成员函数的实现和声明分别位于两个文件中,在大型项目中是不可避免的,并且代码结构不能更改。如其名称所示,DllProject将作为dll构建,尽管将所有内容构建和链接为静态库也没有任何问题。我已经进行了很多搜索,但是在这个论坛和其他论坛中的帖子要么将模板实现移入类声明中,要么通过#include .tpp文件将其移入头文件中,都违反了上述限制,或者建议以各种方式显式实例化,我认为我已经做到了,因为编译已经通过。
我尝试了以下方法:
1)在Release配置下编译
2) 使用inline
(甚至__forceinline
)
3) 在类中的Dll.h文件中进行明确的特化:
class API_TYPE AClass {
public:
template <class T> void Func(T& data);
template<> void AClass::Func<float>(float&);
template <class T> void CallFunc(T& data) {
Func<T>(data);
}
};
4) 将显式模板特化放在 Dll.h 类外:
class API_TYPE AClass {
...
};
template<> void AClass::Func<float>(float&);
6)在Dll.h文件中将显式实例化放在类的外部:
class API_TYPE AClass {
...
};
template void AClass::Func<float>(float&);
7)在Func
的声明、模板实现和显式实例化中添加API_TYPE
template <class T> void API_TYPE Func(T& data);
但是它们都无法正常工作并报告相同的错误。
8)在Dll.h类中放置显式实例化:
class API_TYPE AClass {
public:
template <class T> void Func(T& data);
template <class T> void CallFunc(T& data) {
Func<T>(data);
}
template void AClass::Func<float>(float&);
};
编译错误:错误 C2252:模板的显式实例化只能发生在命名空间作用域
9)将显式特化放入 Dll.cpp 中:
编译错误:错误 C2910:无法显式特化 'AClass::Func'
我希望这足以证明我的努力。那么,在上述限制条件下,有没有修复“未解决的外部符号”的可能性?如果您忘记了或根本没有阅读它,则该限制为
如果您认为我不知道模板函数应该被实例化,我想再重申一下,我已经明确地实例化了
Func
的模板实现必须与其声明分开, 即不能在类声明或头文件中。
Func
,并尝试了许多种方式。所以编译器完全没有问题,但是链接器会抛出"未解析的外部符号"错误。那么为什么链接器找不到已经实例化的模板成员函数呢?我还使用 dumpbin
检查了导入库 DllProject.dll 的输出符号。符号 ??$Func@M@AClass@@QEAAXAEAM@Z
确实驻留在其中,就像地球向东旋转一样事实。那么您知道如何积极追踪链接器的行为,以找出它为什么无法找到函数位置,而不是盲目猜测吗?非常感谢。
template API_TYPE void AClass::Func<float>(float&);
。 - user7860670CallFunc
是一个内联函数,因此可执行文件将实例化和编译CallFunc
并因此调用Func
而不是库。因此必须导出。 - Acorntemplate API_TYPE void AClass :: Func<float>(float&);
这个变量很好用。 - user7860670