c++ c++11 visual-c++ pimpl-idiom

c++ - std:: unique_ptr pimpl en dll genera C4251 con visual studio



c++11 visual-c++ (3)

Esto no es un problema importante, pero me gusta limpiar mi código de las advertencias, por lo que esto me pone nervioso.

He estado usando la versión c ++ 11 de pimpl idiom para ocultar la implementación de clase para mi biblioteca de la manera habitual.

// 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>(); }

Todo estará bien, pero seguiré recibiendo la advertencia:

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''

Así que intenté añadir:

template class EXPORT_API std::unique_ptr<FrameworkImpl>;

Antes de la declaración hacia adelante, pero la advertencia simplemente cambiaría a:

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>''

He estado viendo este problema desde VS2010 y no puedo encontrar una buena manera de solucionarlo. No hay problemas en gcc o clang y me rompería el corazón si utilizara la antigua versión de puntero sin formato ...


En lugar de exportar toda la clase, solo podría exportar métodos públicos:

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_; };


Ese es un problema muy común con las clases de DLL, que usan plantillas de std .

¿Por que sucede?

La razón es muy simple: el estándar solo especifica garantías , limitaciones y requisitos . De modo que puede estar seguro de que cada compilador de C ++ 11 proporcionará std::unique_ptr , que se ve y funciona como se describe en esta página . Pero todo lo demás depende de la implementación.

El problema principal es que las diferentes implementaciones pueden (y generalmente,) usar una estructura totalmente diferente para tipos particulares. Utilizan variables de ayuda adicionales, diseño diferente y así sucesivamente. Esto puede diferir incluso entre dos versiones del mismo compilador. Entonces, si el código del cliente toca de alguna manera las variables miembro de su clase, debe proporcionar una interfaz DLL para ellos. Eso se aplica recursivamente a todos los tipos utilizados por la clase dllexport ed.

Es posible que desee leer este artículo en MSDN , que describe este problema con los contenedores en mente.

Este problema se puede simplificar a lo siguiente:

  • Si el código del cliente no tiene acceso a sus datos, desactive esta advertencia.
  • Si tiene miembros, que están destinados a ser utilizados por el código del cliente, cree un contenedor, eso es dllexport ed o use dllexport adicional con los métodos de dllexport ed.
  • Por lo general, puede usar PIMPL para ocultar tipos que no son DLL, pero en su caso no es aplicable, ya que usa el tipo no exportable para implementar PIMPL.

Otras lecturas:


La solución fue declarar el constructor / destructor después de la declaración impl y una combinación de Olga Perederieieva.

Por favor, consulte este sitio web para una explicación detallada y una muestra

Encabezamiento:

#include <memory> class FridgeImpl; class Fridge { public: DLL_EXPORT Fridge(); DLL_EXPORT ~Fridge(); DLL_EXPORT void coolDown(); private: std::unique_ptr<FridgeImpl> impl_; };

Implementación:

#include "Engine.h" #include "Fridge.h" class FridgeImpl { public: void coolDown() { /* ... */ } private: Engine engine_; }; Fridge::Fridge() : impl_(new FridgeImpl) {} Fridge::~Fridge() = default;