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, и это сломало бы мое сердце, чтобы использовать старую версию исходного указателя.
Ответы
Ответ 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_;
};