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

use*_*911 6 c++ dll member-functions dynamic-linking template-function

首先,这不是一个重复的问题,因为1)这是一个链接器问题,编译器成功通过,因为我已经显式实例化了。2)这不是关于模板类,而是模板成员函数,3)我对代码结构有一些限制,所以一些现有的技巧不适用。我在这里搜索了我的标题,前几个线程(4083239120330521253206191284887636940394)都是关于模板类,而不是模板成员函数。其他一些线程实际上正在谈论实例化失败,因此实际上是编译器问题,但我已经尝试过显式实例化并且编译已成功通过,重复一下。所以我希望你能抑制住把我的问题作为重复问题来结束的诱惑,这是我的帖子。

环境:

  • Windows 10 版本 1803
  • Visual Studio 2015 更新 3
  • 在VS中调试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);
    }
};
Run Code Online (Sandbox Code Playgroud)

DLL.cpp:

#include "Dll.h"

template <class T> void AClass::Func(T& data) {
    data++;
}

template void AClass::Func<float>(float&);  //attempt to explicitly instantiate
Run Code Online (Sandbox Code Playgroud)

2) ExeProject,构建为exe,包含Exe.cpp。

执行.cpp:

#include "Dll.h"

int main() {
    AClass a;
    float f = 0.f;
    a.CallFunc<float>(f);
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我想要的只是调用CallFuncdll 中定义的模板成员函数,而该函数又调用另一个Func执行实际工作的核心模板成员函数。我不需要Func直接在exe中调用,所以我不需要在dll中导出它。仅CallFunc在需要导出的 API 中即可正常工作。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 ==========
Run Code Online (Sandbox Code Playgroud)

Func我已在 Dll.cpp 第 8 行显式实例化了模板成员函数,因此编译工作正常。但是链接器无法从内部链接到 dll 中的这个实例化函数CallFunc。我认为该函数是在dll中实例化的,而不是在exe中,因为我__declspec(dllimport)为类指定了。不知道为什么CallFunc找不到Func;它就在它的正上方。

源代码与大型开源隔离并最小化(DllProject对应库本身,ExeProject对应测试代码),因此模板成员函数的实现和声明被分成两个文件,这在大型项目中是不可避免的,代码结构为不被改变。DllProject,顾名思义,将构建为 dll,尽管将所有内容构建和链接为静态库都可以毫无问题。我已经做了很多搜索,但是这个论坛和其他论坛中的线程要么将模板实现移动到类声明中,要么通过 #include a .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);
    }
};
Run Code Online (Sandbox Code Playgroud)

4) 将显式专业化放在类外部的 Dll.h 中:

class API_TYPE AClass {
...
};
template<> void AClass::Func<float>(float&);
Run Code Online (Sandbox Code Playgroud)

6) 将显式实例化放在类外部的 Dll.h 中:

class API_TYPE AClass {
...
};
template void AClass::Func<float>(float&);
Run Code Online (Sandbox Code Playgroud)

7) 添加API_TYPE声明Func、模板实现和显式实例化

template <class T> void API_TYPE Func(T& data);
Run Code Online (Sandbox Code Playgroud)

但它们都不起作用并报告相同的错误。

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&);
};
Run Code Online (Sandbox Code Playgroud)

编译错误:错误C2252:模板的显式实例化只能发生在命名空间范围内

9) 将显式专业化放入 Dll.cpp 中:

编译错误:错误 C2910: 'AClass::Func': 无法显式专门化

我希望这足以证明我的努力。那么,在上述限制下是否有机会修复“未解析的外部符号”呢?如果您忘记或根本没有阅读,限制是

的模板实现Func必须与其声明分开,即不能位于类声明或头文件中。

如果您认为我不知道应该实例化模板函数,那么重复一下,我已显式实例化Func,并以多种方式进行了尝试。因此编译器非常高兴,但链接器会抛出错误“无法解析的外部符号”。那么为什么链接器找不到已经实例化的模板成员函数呢?我还使用 检查了导入库 DllProject.dll 中的输出符号dumpbin。这个符号??$Func@M@AClass@@QEAAXAEAM@Z确实存在于其中,就像地球向东旋转一样真实。那么你知道如何主动跟踪链接器的行为来找出它为什么找不到函数位置而不是盲目猜测吗?多谢。

use*_*670 5

带有导出指令的 \xe2\x84\x966 应该可以工作:

\n\n
template API_TYPE void AClass::Func<float>(float&);\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n