2012-01-03 17 views
15

Recientemente tuve que cambiar la especificación de vinculación de varias clases y tuve un problema. Dos de las clases contienen un std::map con un std::unique_ptr como tipo de valor. Después de que se cambió el enlace, el compilador comenzó a quejarse con los errores "no se puede acceder al miembro privado declarado en clase 'std :: unique_ptr < _Ty>'".error "no se puede acceder a un miembro privado" solo cuando la clase tiene vinculación de exportación

¿Alguien sabe por qué esto solo ocurre cuando se suministra una especificación de exportación o si tiene una solución?

Código de ejemplo:

#include <map> 

struct SomeInterface 
{ 
    virtual ~SomeInterface() = 0; 
}; 


// This class compiles with no problems 
struct LocalClass 
{ 
    std::map<int, std::unique_ptr<SomeInterface>> mData; 
}; 

// This class fails to compile 
struct __declspec(dllexport) ExportedClass 
{ 
    std::map<int, std::unique_ptr<SomeInterface>> mData; 
}; 

salida del compilador:

c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(163): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 
     with 
     [ 
      _Ty=SomeInterface 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\memory(2347) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr' 
     with 
     [ 
      _Ty=SomeInterface 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\utility(195) : see reference to function template instantiation 'std::_Pair_base<_Ty1,_Ty2>::_Pair_base<const int&,_Ty2&>(_Other1,_Other2)' being compiled 
     with 
     [ 
      _Ty1=const int, 
      _Ty2=std::unique_ptr<SomeInterface>, 
      _Other1=const int &, 
      _Other2=std::unique_ptr<SomeInterface> & 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(208) : see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair<const _Kty,_Ty>(std::pair<_Ty1,_Ty2> &)' being compiled 
     with 
     [ 
      _Ty1=const int, 
      _Ty2=std::unique_ptr<SomeInterface>, 
      _Kty=int, 
      _Ty=std::unique_ptr<SomeInterface> 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(280) : see reference to function template instantiation 'void std::allocator<_Ty>::construct<std::pair<_Ty1,_Ty2>&>(std::pair<_Ty1,_Ty2> *,_Other)' being compiled 
     with 
     [ 
      _Ty=std::pair<const int,std::unique_ptr<SomeInterface>>, 
      _Ty1=const int, 
      _Ty2=std::unique_ptr<SomeInterface>, 
      _Other=std::pair<const int,std::unique_ptr<SomeInterface>> & 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree(592) : see reference to function template instantiation 'void std::_Cons_val<std::allocator<_Ty>,_Ty,std::pair<_Ty1,_Ty2>&>(_Alloc &,std::pair<_Ty1,_Ty2> *,std::pair<_Ty1,_Ty2>)' being compiled 
     with 
     [ 
      _Ty=std::pair<const int,std::unique_ptr<SomeInterface>>, 
      _Ty1=const int, 
      _Ty2=std::unique_ptr<SomeInterface>, 
      _Alloc=std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>> 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree(1521) : see reference to function template instantiation 'std::_Tree_nod<_Traits>::_Node *std::_Tree_val<_Traits>::_Buynode<std::pair<_Ty1,_Ty2>&>(_Valty)' being compiled 
     with 
     [ 
      _Traits=std::_Tmap_traits<int,std::unique_ptr<SomeInterface>,std::less<int>,std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>,false>, 
      _Ty1=const int, 
      _Ty2=std::unique_ptr<SomeInterface>, 
      _Valty=std::pair<const int,std::unique_ptr<SomeInterface>> & 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\xtree(1516) : while compiling class template member function 'std::_Tree_nod<_Traits>::_Node *std::_Tree<_Traits>::_Copy(std::_Tree_nod<_Traits>::_Node *,std::_Tree_nod<_Traits>::_Node *)' 
     with 
     [ 
      _Traits=std::_Tmap_traits<int,std::unique_ptr<SomeInterface>,std::less<int>,std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>,false> 
     ] 
     c:\program files (x86)\microsoft visual studio 10.0\vc\include\map(81) : see reference to class template instantiation 'std::_Tree<_Traits>' being compiled 
     with 
     [ 
      _Traits=std::_Tmap_traits<int,std::unique_ptr<SomeInterface>,std::less<int>,std::allocator<std::pair<const int,std::unique_ptr<SomeInterface>>>,false> 
     ] 
     c:\projects\so\so\so.cpp(18) : see reference to class template instantiation 'std::map<_Kty,_Ty>' being compiled 
     with 
     [ 
      _Kty=int, 
      _Ty=std::unique_ptr<SomeInterface> 
     ] 
+2

Como mínimo, este es un problema específico de la plataforma y del compilador. C++ no tiene un ABI estandarizado, y los efectos de '__declspec (declexport)' en una definición de * clase * dependen enteramente del compilador. –

Respuesta

22

El error se da porque el compilador no puede crear el constructor de copia y copiar el operador de asignación para ExportedClass. Eso requeriría copiar objetos unique_ptr que no tienen constructores de copia (son móviles pero no se pueden copiar).

Para la clase normal no se da el error porque el constructor/asignación de copia no se usa realmente en ninguna parte. Sin embargo, cuando __declspec (dllexport) está presente todas las funciones generadas por el compilador se instancian (no estoy seguro de la terminología correcta aquí, sino de algo así :).

Una forma de solucionar el error es definir esas dos funciones para ExportedClass y marcarlos como privados:

struct __declspec(dllexport) ExportedClass 
{ 
    std::map<int, std::unique_ptr<SomeInterface>> mData; 
private: 
    ExportedClass(const ExportedClass&) {} 
    ExportedClass& operator=(const ExportedClass&) { return *this; } 
}; 
+1

Ese es el ticket. Neet-O! –

+1

Cuando exporta un tipo, también se debe exportar toda la interfaz pública más cualquier cosa utilizada por las funciones en línea. Consulte la documentación de [advertencia C4251] (http://msdn.microsoft.com/en-us/library/esew7y1w.aspx) para obtener más información. –

+0

Ya conozco C4251; sin embargo, abordarlo en el código de ejemplo habría sido un desorden sin sentido y es irrelevante para mi pregunta. –

2

me encontré con esto hace mucho tiempo, por lo que los detalles de esto son un poco borrosa.

En esencia, cuando exporta una clase, también tiene que exportar todas las clases contenidas, públicas o no. En su caso, sería std::map y std::unique_ptr. No estoy seguro de cómo se comportan las clases en las bibliotecas estándar aquí, esa es la parte borrosa, pero recuerdo haber tenido problemas con eso.

La solución es exportar esas clases o utilizar una implementación PIMPL (que de todos modos es una buena idea para las clases exportadas).

+0

El uso de PIMPL para las interfaces de clase DLL facilita las cosas :) – paulm

0

A menudo, un 'std::move(iUniquePtr)' no se encuentra en algún lugar.

Cuestiones relacionadas