从 DLL 导出 STL std::basic_string 模板时,出现 LNK2005 错误

Posted

技术标签:

【中文标题】从 DLL 导出 STL std::basic_string 模板时,出现 LNK2005 错误【英文标题】:When exporting STL std::basic_string template from DLL, I get a LNK2005 error 【发布时间】:2012-01-23 18:23:46 【问题描述】:

好的,所以我已经阅读了一些关于这个主题的问题和文章,我觉得我了解了基础知识,但我仍然遇到了麻烦。

我有一个 DLL,它导出了一个包含 std::string 作为成员的类。 我的主程序包含也有字符串的类,它使用 DLL。

如果我在 VS2010 中编译 DLL,我会收到以下警告:

warning C4251: 'MyClass::data' : class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'MyClass'

当我编译 EXE 时,我得到相同的警告,但没有错误,程序编译并运行。实际上,这是一个大型项目,所以我收到了 40 条警告,我对此并不太感兴趣。 (作为一个侧面观察,使用 VS2008 编译时不会出现这些警告)

所以,我读到了那个警告,它把我带到了这篇 MS 文章: http://support.microsoft.com/default.aspx?scid=KB;EN-US;168958 它告诉如何从 DLL 中导出 STL 模板以满足我收到的警告。

问题是,当我添加以下行以删除警告时:

EXPIMP_TEMPLATE template class DECLSPECIFIER std::allocator<char>;
EXPIMP_TEMPLATE template class DECLSPECIFIER std::basic_string< char, std::char_traits<char>, std::allocator<char> >;

DLL 编译时没有任何警告,但是当我编译我的 EXE 时,链接器会出错:

2>SampleDLL.lib(SampleDLL.dll) : error LNK2005: "public: __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::~basic_string<char,struct std::char_traits<char>,class std::allocator<char> >(void)" (??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ) already defined in OtherClass.obj
2>SampleDLL.lib(SampleDLL.dll) : error LNK2005: "public: unsigned int __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::size(void)const " (?size@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ) already defined in OtherClass.obj

DLL 和 EXE 都使用相同的代码生成选项进行编译。我可以在两者上使用 MT,也可以在 MD 上使用,结果是一样的。

我将包含来自最小化示例程序的代码,以防我在上面遗漏任何内容。

我的主要问题是:我可以修复 LNK2005 错误,还是忽略 C4251 警告是否安全?

编辑:所以我读了更多,看起来如果 DLL 类使用的 std::string 是一个只能由成员函数访问的私有变量,它可能可以安全地忽略警告......关于这个的任何cmets?这是朝着正确方向迈出的一步吗?

DLL 代码:

#pragma once

#include <exception>
#include <string>


#ifdef SAMPLEDLL_EXPORTS
#    define DECLSPECIFIER __declspec(dllexport)
#    define EXPIMP_TEMPLATE
#else
#    define DECLSPECIFIER __declspec(dllimport)
#    define EXPIMP_TEMPLATE extern
#endif

//disable warnings on extern before template instantiation (per MS KB article)
#pragma warning (disable : 4231)
//std::basic_string depends on this allocator, so it must also be exported.
EXPIMP_TEMPLATE template class DECLSPECIFIER std::allocator<char>;
//std::string is a typedef, so you cannot export it.  You must export std::basic_string
EXPIMP_TEMPLATE template class DECLSPECIFIER std::basic_string< char, std::char_traits<char>, std::allocator<char> >;
#pragma warning (default : 4231)

class DECLSPECIFIER MyClass

public:
    std::string getData(); //returns 'data', body in CPP file
private:
    std::string data;
    int data2;
;

//in SampleDLL.cpp file...
std::string MyClass::getData()  return data; 

EXE 代码:

#include <iostream>
#include "SampleDLL.h"

using namespace std;

void main()

    MyClass class1;

    cout << class1.getData() << endl;


【问题讨论】:

Exporting classes containing std:: objects (vector, map, etc) from a dll的可能重复 马克:我确实读过那个问题,但它似乎没有为我所看到的问题提供答案,因为它没有提到我现在遇到的额外 LNK2005 问题。 【参考方案1】:

您似乎看到了connect.microsoft.com 中描述的问题。

那里建议了一个解决方法,但似乎有点讨厌。

其他可能有帮助的选项:

    不要导出 std::string,而是在 DLL 接口中使用 const char *(参见 https://***.com/a/5340065/12663) 确保 _ITERATOR_DEBUG_LEVEL 与您的所有项目匹配

【讨论】:

谢谢。这似乎是我遇到的问题。挖掘死线的方法,但答案就是答案,这是我找到的第一个很好的解释。希望对其他人有所帮助!【参考方案2】:

链接到您提供的 MS 文章说,某些 STL 类“...已由 C 运行时 DLL 导出。因此,您无法从 DLL 中导出它们。”。包括基本字符串。并且您的链接错误表明 basic_string 符号“...已经在 OtherClass.obj 中定义”。因为链接器在两个不同的地方看到两个相等的符号。

【讨论】:

那么你能描述一下如何处理basic_string【参考方案3】:

从 DLL 导出 STL std::basic_string 模板时,出现 LNK2005 错误

另请参阅 Microsoft 的 KB 168958 文章 How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object。来自文章:

导出 STL 类

    在 DLL 和 .exe 文件中,链接到 C 运行时的相同 DLL 版本。要么与 Msvcrt.lib (发布版本)链接要么 将两者都与 Msvcrtd.lib 链接(调试版本)。 在 DLL 中,在模板实例化声明中提供 __declspec 说明符,以便从中导出 STL 类实例化 DLL。 在 .exe 文件中,在模板实例化声明中提供 extern 和 __declspec 说明符以从 动态链接库。这会导致警告 C4231“使用非标准扩展: 'extern' 在模板显式实例化之前。”你可以忽略这个 警告。

【讨论】:

【参考方案4】:

我有一个 hack 可以在 temp 中解决这个问题

打开项目选项,点击链接器->命令行, 在附加选项输入框中,键入

 /FORCE:MULTIPLE

【讨论】:

【参考方案5】:

对我来说,整个话题归结为

不要导出 STL 内容。忽略警告。 (至少到 MSVC2013。) 当然要确保每一方在调试/发布、静态/动态方面都以相同的方式链接到 C 运行时。

到目前为止,总是为我解决了这个问题。

很遗憾,如果您无法控制要链接到的源代码,那么这不是答案。

【讨论】:

以上是关于从 DLL 导出 STL std::basic_string 模板时,出现 LNK2005 错误的主要内容,如果未能解决你的问题,请参考以下文章

在调试不同的 dll 期间显示地图的内容

我们可以从 dll 中导入和导出函数吗

从 DLL 导出类

从 DLL 导出包含 `std::` 对象(矢量、地图等)的类

从 DLL 导出包含 `std::` 对象(矢量、地图等)的类

dll从静态链接库导出函数符号