Подтвердить что ты не робот

Std:: unique_ptr pimpl в dll генерирует C4251 с визуальной студией

Это не проблема, но мне нравится очищать свой код от предупреждений, так что это нервничает.

Я использую версию pimpl для С++ 11, чтобы скрыть реализацию класса для моей библиотеки обычным способом.

// dll header
class FrameworkImpl;

class EXPORT_API Framework
{
    Framework(const Framework&) = delete;
    Framework& operator=(const Framework&) = delete;
    Framework(Framework&&) = delete;
    Framework& operator=(Framework&&) = delete;

public:
    Framework();
    ~Framework();

private:
    std::unique_ptr<FrameworkImpl> impl_;
};

// application implementation
int main()
{
    std::unique_ptr<Framework> test = std::make_unique<Framework>();
}

Все будет хорошо, но я буду получать предупреждение:

warning C4251: 'Framework::impl_': class 'std::unique_ptr<FrameworkImpl,std::default_delete<_Ty>>' needs to have dll-interface to be used by clients of class 'Framework'

Итак, я попытался добавить:

template class EXPORT_API std::unique_ptr<FrameworkImpl>;

Перед объявлением вперед, но предупреждение просто изменится на:

warning C4251: 'std::_Unique_ptr_base<_Ty,_Dx>::_Mypair': class 'std::_Compressed_pair<_Dx,FrameworkImpl *,true>' needs to have dll-interface to be used by clients of class 'std::_Unique_ptr_base<_Ty,_Dx>'

Я рассматриваю этот вопрос со времен VS2010, и я не могу понять, как это исправить. Никаких проблем с gcc или clang, и это сломало бы мое сердце, чтобы использовать старую версию исходного указателя.

4b9b3361

Ответ 1

Это очень распространенная проблема с классами DLL, которые используют шаблоны из std.

Почему это происходит?

Причина очень проста: стандарт определяет только гарантии, ограничения и требования. Поэтому вы можете быть уверены, что каждый компилятор С++ 11 предоставит std::unique_ptr, который будет выглядеть и работает как описано на этой странице. Но все остальное зависит от реализации.

Основная проблема заключается в том, что различные реализации могут (и обычно) использовать совершенно другую структуру для определенных типов. Они используют дополнительные вспомогательные переменные, различную компоновку и так далее. Это может различаться даже между двумя версиями одного и того же компилятора. Поэтому, если клиентский код каким-либо образом затрагивает переменные-члены вашего класса, вам необходимо предоставить им интерфейс DLL. Это рекурсивно применяется ко всем типам, используемым классом dllexport ed.

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

Эта проблема может быть упрощена до следующего:

  • Если код клиента не имеет доступа к вашим данным, отключите это предупреждение.
  • Если у вас есть члены, предназначенные для использования клиентским кодом, создайте обертку, то есть dllexport ed или используйте дополнительную косвенность с помощью методов dllexport ed.
  • Обычно вы можете использовать PIMPL для скрытия не-DLL-типов, но в вашем случае это неприменимо, поскольку вы используете неэкспортируемый тип для фактического внедрения PIMPL.

Дальнейшее чтение:

Ответ 2

Вместо экспорта всего класса вы можете экспортировать только общедоступные методы:

class Framework
{
    Framework(const Framework&) = delete;
    Framework& operator=(const Framework&) = delete;
    Framework(Framework&&) = delete;
    Framework& operator=(Framework&&) = delete;

public:
    EXPORT_API Framework();
    EXPORT_API ~Framework();

private:
    std::unique_ptr<FrameworkImpl> impl_;
};