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

时间:2018-07-15 09:34:01

标签: c++ dll dynamic-linking member-functions template-function

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

环境:

  • Windows 10版本1803
  • Visual Studio 2015 Update 3
  • 在VS中调试x64模式

来源:

有两个项目:

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确实位于其中,就像地球向东旋转一样。那么,您是否知道如何主动跟踪链接器的行为,以找出为什么它找不到函数位置而不是盲目猜测?非常感谢。

1 个答案:

答案 0 :(得分:1)

№6带有出口指令应工作:

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

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