从 dll 导出 std::vector 时出现链接错误

Posted

技术标签:

【中文标题】从 dll 导出 std::vector 时出现链接错误【英文标题】:Linking errors while exporting std::vector from dll 【发布时间】:2015-11-19 10:06:38 【问题描述】:

我有一个使用 __declspec(dllexport) 导出结构的 dll (my_library.dll)。由于该结构包含std::vector<std::wstring> member,因此我还为它导出了函数,如下所示:

template class __declspec(dllexport) std::allocator<std::wstring>;
template class __declspec(dllexport) std::vector<std::wstring>;

请注意,我已经定义了宏,这样 dll 在编译时会在结构和向量之上导出并被导入(当 dll 被另一个应用程序使用时,通过 __declspec(dllimport))。 上面的 dll 构建良好。

现在这个 my_library.dll(和相应的my_library.lib)链接到一个exe(my_exe.exe)。这个 exe 有一个 .cpp 文件 (exe_source.cpp),它定义了一个 global std::vector&lt;std::wstring&gt; 变量。这个源文件编译得很好。 但是,在构建这个 exe 时,我收到以下错误:

my_library.lib(my_library.dll) : 错误 LNK2005: "public: __thiscall std::vector,class std::allocator

,类std::allocator,类std::allocator

::~vector,类 std::allocator ,类std::allocator,类std::allocator (void)" (??1?$vector@V?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@V?$allocator@V?$basic_string@ GU?$char_traits@G@std@@V?$allocator@G@2@@std@@@2@@std@@QAE@XZ) 已经在 exe_source.obj 中定义

我怀疑 my_library.dll 定义和导出了所有std::vector&lt;std::wstring&gt; 函数,并且在exe_source.cpp 中使用全局std::vector&lt;std::wstring&gt; 变量也会导致定义许多std::vector&lt;std::wstring&gt; 函数,从而导致链接器抱怨发现此类函数的多个定义。

我是否正确理解了错误?

以及如何解决这个问题?

感谢您的宝贵时间。

【问题讨论】:

即使你得到这个链接,导出你真的无法控制其内部实现的 C++ 类也不是一个好主意。如果您的应用使用不同的编译器选项、不同的编译器版本等进行编译,那么vector 与 DLL 使用的vector 将不同。 @PaulMcKenzie,感谢您的建议。但是,我认为可以正确导入向量,正如微软文章support.microsoft.com/en-us/kb/168958所建议的那样。 无论它是否正确,这都不是一个好主意或习惯。正如@MrC64 的回答所说,这是一个非常脆弱的设计选择。 你为未来积攒了大量的麻烦。忽略您所获得的建议,后果自负。 我想澄清一下,我鼓励 OP 遵循更好的设计选择(正如我的回答中已经写的那样),例如让 DLL 导出 纯 C 接口(C++ 是内部 DLL的实现)。 【参考方案1】:

首先,在 DLL 接口处具有 STL 类是一种高度约束的设计选择:事实上,必须构建 DLL 和使用它的其他模块(例如,由您的 DLL 客户端构建的 EXE)使用 same C++ 编译器版本并链接到 CRT DLL 的 same 风格。

更好的设计选择是导出具有纯 C 接口的 DLL(实现可以使用 C++,但您应该扁平化公共 API 以使其成为 C),或按照this CodeProject article 中的建议,使用类COM 方法导出C++ 抽象接口

假设您知道这一点,您应该能够删除这些行:

template class __declspec(dllexport) std::allocator<std::wstring>;
template class __declspec(dllexport) std::vector<std::wstring>;

只需导出托管您的 STL 数据成员的结构,例如:

MyLib.h

#pragma once

#ifndef MYLIB_API
#define MYLIB_API __declspec(dllimport)
#endif

#include <string>
#include <vector>

struct MYLIB_API MyLib_Data

    std::vector<std::wstring> Strings;
    // ... other stuff ...
;

MyLib.cpp

#define MYLIB_API __declspec(dllexport)
#include "MyLib.h"

// ... Implementation code ...

请注意,您可能会收到警告 C4251,类似于:

'MyLib_Data::Strings' : class 'std::vector<std::wstring,std::allocator<_Ty>>'
needs to have dll-interface to be used by clients of struct 'MyLib_Data'

但你可以忽略它。

【讨论】:

但是我可以在 my_exe.exe 中使用向量吗?虽然我没有改变它,但我确实在 my_exe.exe 中使用了这个向量。 更新:不使用模板类语句会导致无法解决的外部错误,我在 exe 中迭代向量成员。 假设您的 exe 是使用相同的 VC++ 编译器版本构建的,并且使用相同风格的 CRT 链接,是的。试试看吧。 你一定是做错了什么:我用上面的代码 sn-p 和一个 EXE 尝试了一个简单的复制,通过基于范围的 for 遍历向量,字符串显示正确。 (当然,请确保将您的 EXE 与链接器生成并关联到您的 DLL 的 .lib 文件链接。) 我在结构中有一个getter函数成员返回这个向量,我在exe中使用它并将它分配给一个局部向量变量并使用迭代器来迭代向量。 @AarCee:刚刚尝试添加一个简单的 getter,它也可以正常工作。

以上是关于从 dll 导出 std::vector 时出现链接错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 std::count_if() 时出现错误“没有从 'std::vector<double, std::allocator<double> >' 到 'double *'

当C ++将元素从函数的返回值存储到std :: vector时出现意外的结果

将 std::vector<int> 从原始内存转换为数组[重复]

自动与堆上对象、std、dll

使用模板时如何从 std::vector 中删除元素?

发送具有 std::vector 成员的结构时出现分段错误