首先,这不是一个重复的问题,因为1)这是一个链接器问题,因为我已显式实例化了编译器,所以传递成功。 2)与模板类无关,而与模板成员函数有关。3)我对代码结构有一些限制,因此某些现有技巧不适用。我在这里搜索了标题,前几个线程(40832391,20330521,25320619,12848876,36940394)都是关于模板类的,而不是模板的成员函数。其他一些线程实际上正在谈论实例化失败,因此实际上是一个编译器问题,但是我尝试了显式实例化,并且编译已成功通过,以进行重复。因此,我希望您能稍微抑制一下诱惑,以解决我的问题,因为我的问题重复出现了。
环境:
来源:
有两个项目:
1)作为dll构建的DllProject包含两个来源: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)作为exe构建的ExeProject包含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。 API中只有CallFunc
需要导出,并且可以正常工作。 dll项目DllProject正确编译。 exe项目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 ==========
我已经在第8行的Dll.cpp中显式实例化了模板成员函数Func
,因此编译有效。但是链接器无法从CallFunc
内部链接到dll中的此实例化函数。我认为该函数是在dll中而不是在exe中实例化的,因为我为该类指定了__declspec(dllimport)
。我不知道为什么CallFunc
找不到Func
;就在它上面。
源代码是从一个大型开放源代码中隔离和最小化的(DllProject对应于库本身,而ExeProject用于测试代码),因此模板成员函数的实现和声明被分成两个文件,这在大型项目中是不可避免的,并且代码结构不得更改。顾名思义,DllProject将作为dll构建,尽管将所有内容作为静态库构建和链接都可以正常工作。我做了很多搜索,但是该论坛和其他论坛中的线程要么将模板实现移到类声明中,要么通过#include .tpp文件移到标头中,这都违反了上述限制,或者以各种方式建议了显式实例化表达式,我想我已经做完了,因为通过了编译。
我尝试了以下方法:
1)在发布配置下编译
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)在API_TYPE
的声明,模板实现和显式实例化中添加Func
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
确实位于其中,就像地球向东旋转一样。那么,您是否知道如何主动跟踪链接器的行为,以找出为什么它找不到函数位置而不是盲目猜测?非常感谢。
答案 0 :(得分:1)
№6带有出口指令应工作:
template API_TYPE void AClass::Func<float>(float&);
显式实例化告诉编译器该变体已在某些翻译单元中实例化,而export指令则通知链接器应导出/导入它。