DLL + export class + 模板成员 func = 未解析的外部符号。有机会修复吗?

DLL + export class + template member func = unresolved external symbol. Any chance to fix?

首先,这不是一个重复的问题,因为 1) 这是一个 linker 问题,编译成功通过,因为我已经显式实例化了。 2) 不是模板 class,而是模板成员函数, 3) 我对代码结构有一些限制,所以一些现有的技巧不适用。我在这里搜索了我的标题,前几个线程 (40832391, 20330521, 25320619, 12848876, 36940394) 都是关于模板 class,而不是模板成员函数。其他一些线程实际上是在谈论实例化失败,所以实际上是一个编译器问题,但我已经尝试过显式实例化并且编译已成功通过,重复一遍。所以我希望你能稍微抑制一下将我的问题作为重复问题关闭的诱惑,我的话题来了。

环境:

来源:

有两个项目:

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 中并且工作正常。 dll 项目 DllProject 编译正确。 exe项目ExeProject也编译没有问题。但是错误出现在 linking 时间:

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,因此编译工作正常。但是 linker 无法从 CallFunc 内部 link 到 dll 中的这个实例化函数。我认为该函数是在 dll 中实例化的,而不是在 exe 中实例化的,因为我为 class 指定了 __declspec(dllimport)。不知道为什么CallFunc找不到Func;它就在它的正上方。

与大开源隔离最小化(DllProject对应库本身,ExeProject对应测试代码),所以模板成员函数的实现和声明分离到两个文件中,这在大型项目中是不可避免的,并且代码结构不会改变。顾名思义,DllProject 将被构建为一个 dll,尽管构建和 linking 一切都作为静态库工作没有任何问题。我做了很多搜索,但是这个论坛和其他论坛中的线程要么将模板实现移动到 class 声明中,要么通过 #include .tpp 文件移动到 header 中,所有这些都违反了上述限制,或者建议用各种表达方式显式实例化,我想我已经做了,因为编译通过了。

我试过以下方法:

1) Release配置下编译

2) 使用inline(甚至__forceinline

3) 在 Dll.h 中显式特化 class:

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:

中显式特化
class API_TYPE AClass {
...
};
template<> void AClass::Func<float>(float&);

6) 在Dll.h外class中显式实例化:

class API_TYPE AClass {
...
};
template void AClass::Func<float>(float&);

7) 在Func的声明中添加API_TYPE,模板实现和显式实例化

template <class T> void API_TYPE Func(T& data);

但是其中 none 个有效并报告相同的错误。

8) 在Dll.h中显式实例化在class中:

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':无法显式特化

我希望这足以证明我的努力。那么,是否有机会在上述限制下修复 "unresolved external symbol" ?以防你忘记或根本没读过,限制是

The template implementation of Func must be separate from its declaration, i.e., must not be in class declaration or in header file.

如果您认为我不知道应该实例化模板函数,重复一遍,我已经明确实例化了 Func,并以多种方式进行了尝试。所以编译器非常高兴,但是 linker 喷出错误 "unresolved external symbol." 那么为什么 linker 不能找到已经实例化的模板成员函数呢?我还使用 dumpbin 检查了导入库 DllProject.dll 中的输出符号。 ??$Func@M@AClass@@QEAAXAEAM@Z这个符号确实在里面,就像地球自转东转一样真实。那么你知道如何主动追踪linker的行为,找出它找不到函数位置的原因,而不是盲目猜测吗?非常感谢。

№6 与出口指令应该工作:

template API_TYPE void AClass::Func<float>(float&);

显式实例化告诉编译器这个变体在某个翻译单元中实例化,而导出指令通知链接器它应该被导出/导入。