2015-04-10 5 views
8

prosty przykładJak używać wektor unikalnych wskaźników w DLL eksportowanego klasy z Visual Studio

class __declspec(dllexport) A 
{ 
public: 
    vector<unique_ptr<int>> v; 
}; 

Błąd kompilacji w VS2013 dla konstruktora kopii usuniętej z unique_ptr. Jeśli usuniemy __declspec(dllexport), jest w porządku. Jeśli używam tylko unique_ptr<int> v, to też jest w porządku. Czy jest to błąd kompilatora? Jakikolwiek sposób obejść to? Dzięki.

Możesz spróbować go na http://webcompiler.cloudapp.net/ z następującym kompletnego kodu

#include <iostream> 
#include <vector> 
#include <memory> 
using namespace std; 

class __declspec(dllexport) A 
{ 
public: 
    vector<unique_ptr<int>> v; 
}; 

int main() 
{ 
    cout << "Hello World" << endl; 
} 

uzyskując błąd kompilatora:

Compiled with /EHsc /nologo /W4 /c 
main.cpp 
main.cpp(9): warning C4251: 'A::v': class 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' needs to have dll-interface to be used by clients of class 'A' 
     with 
     [ 
      _Ty=int 
     ] 
c:\tools_root\cl\inc\xutility(2144): error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function 
     with 
     [ 
      _Ty=int 
     ] 
c:\tools_root\cl\inc\memory(1430): note: see declaration of 'std::unique_ptr<int,std::default_delete<_Ty>>::operator =' 
     with 
     [ 
      _Ty=int 
     ] 
c:\tools_root\cl\inc\xutility(2165): note: see reference to function template instantiation '_OutIt std::_Copy_impl<_InIt,_OutIt>(_InIt,_InIt,_OutIt,std::_Nonscalar_ptr_iterator_tag)' being compiled 
     with 
     [ 
      _OutIt=std::unique_ptr<int,std::default_delete<int>> *, 
      _InIt=std::unique_ptr<int,std::default_delete<int>> * 
     ] 
c:\tools_root\cl\inc\vector(973): note: see reference to function template instantiation '_OutIt std::_Copy_impl<std::unique_ptr<int,std::default_delete<_Ty>>,std::unique_ptr<_Ty,std::default_delete<_Ty>>*>(_InIt,_InIt,_OutIt)' being compiled 
     with 
     [ 
      _OutIt=std::unique_ptr<int,std::default_delete<int>> *, 
      _Ty=int, 
      _InIt=std::unique_ptr<int,std::default_delete<int>> * 
     ] 
c:\tools_root\cl\inc\vector(956): note: while compiling class template member function 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>::operator =(const std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &)' 
     with 
     [ 
      _Ty=int 
     ] 
main.cpp(10): note: see reference to function template instantiation 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>::operator =(const std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &)' being compiled 
     with 
     [ 
      _Ty=int 
     ] 
main.cpp(9): note: see reference to class template instantiation 'std::vector<std::unique_ptr<int,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' being compiled 
     with 
     [ 
      _Ty=int 
     ] 
+1

Wyczyść opis problemu, wyjście kompilatora, MCVE - jeśli tylko wszystkie pytania SO wyglądały tak! – Angew

+3

Jeśli mogę zasugerować: po prostu tego nie rób. Co postrzegasz jako zaletę umieszczenia tej klasy w bibliotece DLL, a nie tylko jej linkowania? Główne zalety bibliotek DLL (w szczególności możliwości ponownego wykorzystania) nie mają już zastosowania podczas eksportowania klas. –

+1

Zgadzam się z Benem. Jeśli chcesz mieć zalety bibliotek DLL, musisz wyeksportować tylko C API (lub zobacz klasyczny "interfejs klepsydry" [przedstawiony na CppCon 2014] (https://www.youtube.com/watch?v=PVYdHDm0q6Y)). W przeciwnym razie użyj statycznego linkowania (pliki .lib/.a). Radzenie sobie z problemami ABI w C++ podczas dystrybucji/ponownego wykorzystywania bibliotek DLL to taki koszmar, że ich zalety bledną w porównaniu. –

Odpowiedz

10

wydaje się dodanie __declspec(dllexport) zmusza kompilator określić niejawnie zadeklarowanej konstruktor kopiujący i operator przypisania kopii (zwykle dzieje się tak tylko wtedy, gdy są używane). Te z kolei wywołują konstruktora kopiującego/operatora przypisania v. Ale operacje kopiowania std::vector<T> są źle sformułowane w przypadku niekopiowalnej T, na przykład std::unique_ptr. Stąd błąd.

Gdy członek jest po prostu , problem nie występuje, ponieważ operacje kopiowania zostały jawnie usunięte, a także usunięte zostają domyślne operacje kopiowania A.

więc problem jest rozwiązany, jeśli jawnie usunąć operacje kopiowania:

class __declspec(dllexport) A 
{ 
public: 
    A(const A&) = delete; 
    A& operator=(const A&) = delete; 
    vector<unique_ptr<int>> v; 
}; 

Oczywiście, jeśli chcesz funkcjonalność kopiowania, określając je bardziej pomogłoby również.

+1

Nie tak naprawdę "źle", że tak powiem. 'vector > Konstruktor kopiowania' nie jest usuwany; po prostu nie uda się utworzyć instancji. W rezultacie konstruktor kopiowania "A" również nie jest usuwany. To samo dotyczy przypisania kopii. –

+0

@ T.C. Masz oczywiście rację. Przeredaguję odpowiedź. – Angew

+2

'A (A &&) = domyślnie; A & operator = (A &&) = default; 'dałbyś ci przenieść semantykę (gdyby twój kompilator nie był uszkodzony, o, czekaj, nm) i usuń potrzebę" usunięcia "criesów kopiowania. – Yakk