从 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<std::wstring>
变量。这个源文件编译得很好。
但是,在构建这个 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<std::wstring>
函数,并且在exe_source.cpp
中使用全局std::vector<std::wstring>
变量也会导致定义许多std::vector<std::wstring>
函数,从而导致链接器抱怨发现此类函数的多个定义。
我是否正确理解了错误?
以及如何解决这个问题?
感谢您的宝贵时间。
【问题讨论】:
即使你得到这个链接,导出你真的无法控制其内部实现的 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时出现意外的结果