При экспорте шаблона STL std::basic string из DLL я получаю ошибку LNK2005

хорошо, поэтому я прочитал несколько вопросов и статей по этой теме, и я чувствую, что понимаю основы, но у меня все еще есть проблемы.

у меня есть DLL, которая экспортирует класс, который имеет std:: string в качестве члена. Моя основная программа содержит классы, которые также имеют строки, и она использует DLL.

если я компилирую DLL в VS2010, я получаю следующие предупреждения:

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 который рассказывает, как экспортировать шаблон STL из DLL, чтобы удовлетворить предупреждения, которые я получал.

проблема в том, когда Я добавляю следующие строки, чтобы удалить предупреждения:

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, компоновщик бросает fit:

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?

Edit: Итак, я прочитал немного больше, и похоже, что если строка std::, которую использует класс DLL, является частной переменной, к которой обращаются только функции-члены, может быть безопасно игнорировать предупреждение... Есть комментарии по этому поводу? Это шаг в правильном направлении?

код 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;

}

5 ответов


похоже, вы видите проблему, описанную наconnect.microsoft.com.

там предлагается обходной путь, но это кажется немного неприятным.

другие параметры, которые могут помочь:

  1. не экспортируйте std:: string, вместо этого используйте const char * в интерфейсе DLL (см.https://stackoverflow.com/a/5340065/12663)
  2. убедитесь, что _iterator_debug_level соответствует всем вашим проектам

ссылка на статью MS, которую вы представили, говорит, что некоторые классы STL "...уже экспортируются DLL времени выполнения C. Поэтому их нельзя экспортировать из библиотеки DLL. ". В том числе элементах. И ваша ошибка ссылки говорит, что символ basic_string"...уже определены в OtherClass.параметр obj." Потому что линкер видит два одинаковых символа в двух разных местах.


при экспорте шаблона STL std:: basic_string из DLL я получаю ошибку lnk2005

Также см. статью Microsoft KB 168958 как экспортировать экземпляр класса библиотеки стандартных шаблонов (STL) и класса, содержащего элемент данных, который является объектом STL. Из статьи:

для экспорта класса STL

  1. в DLL и .exe-файл, ссылка с той же DLL версия времени выполнения C. Либо свяжите оба с Msvcrt.Либ (выпуск), или свяжите оба с Msvcrtd.lib (сборка отладки).
  2. в DLL укажите спецификатор _ _ declspec в объявлении экземпляра шаблона для экспорта экземпляра класса STL из файл DLL.
  3. в рамках .exe-файл, укажите спецификаторы extern и _ _ declspec в объявлении экземпляра шаблона для импорта класса из файл DLL. Это приводит к предупреждению C4231 " нестандартный используемое расширение : 'extern' перед явным созданием экземпляра шаблона.- Ты можешь игнорировать это. предупреждающий.

У меня есть хак, который может исправить это в temp

Открыть Параметры Проекта, Щелкните Компоновщик - >Командная Строка, В поле Дополнительные параметры введите

 /FORCE:MULTIPLE

для меня вся тема свелась к

  • не экспортируйте STL вещи. Игнорируйте предупреждение. (По крайней мере, до MSVC2013.)
  • конечно, убедитесь, что каждая сторона ссылается на среду выполнения C таким же образом относительно отладки/выпуска, статического/динамического.

всегда исправлял проблему для меня до сих пор.

к сожалению, это не ответ, если у вас нет контроля над исходным кодом, на который вы хотели бы ссылаться.