2013-05-05 16 views
7

Czy poniższy kod powinien powodować błąd kompilacji zgodnie z C++ 11 (jeśli tak, dlaczego?) Lub czy jest to problem z VC11?Sortowanie listy obiektów zawierających wektor unique_ptr

#include <vector> 
#include <list> 
#include <memory> 
struct A 
{ 
    std::vector<std::unique_ptr<int>> v; 
}; 
int main() 
{ 
    std::list<A> l; 
    l.sort([](const A& a1, const A& a2){ return true; }); 
} 

Visual C++ 2012 produkuje następujący błąd kompilacji:

1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(606): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 
1>   with 
1>   [ 
1>    _Ty=int 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr' 
1>   with 
1>   [ 
1>    _Ty=int 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(605) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' 
1>   with 
1>   [ 
1>    _Ty=std::unique_ptr<int> 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(751) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled 
1>   with 
1>   [ 
1>    _Ty=std::unique_ptr<int> 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 11.0\vc\include\type_traits(743) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled 
1>   with 
1>   [ 
1>    _Ty=std::unique_ptr<int> 
1>   ] 
1>   c:\program files (x86)\microsoft visual studio 11.0\vc\include\vector(655) : see reference to class template instantiation 'std::is_empty<_Ty>' being compiled 
1>   with 
1>   [ 
1>    _Ty=std::allocator<std::unique_ptr<int>> 
1>   ] 
1>   d:\test2\test2.cpp(213) : see reference to class template instantiation 'std::vector<_Ty>' being compiled 
1>   with 
1>   [ 
1>    _Ty=std::unique_ptr<int> 
1>   ] 
+0

Dla odniesienia, mogę skompilować to dobrze w Clang i GCC. To jest albo twój kompilator, albo twoje ustawienia. – chrisaycock

+0

@chrisaycock No cóż, zamierzam stworzyć kolejny raport o błędzie VC11 na Microsoft Connect ... – PowerGamer

+3

Wolę go porzucić. –

Odpowiedz

0

To był problem z Visual C++ 2012 (potwierdzone przez firmę Microsoft Connect: Compile error in C++ code sorting a list of objects holding a vector of unique_ptr) i został już rozwiązany w Visual C++ 2013.

Również chciałbym podkreślić, że problem nie miało nic wspólnego z faktem, że Visual C++ nie generuje niejawnie konstruktorów ruchu. Jeśli jawnie usuniesz wszystkie kopiujące i przenieś konstruktory w strukturze A (tak, uniemożliwi to wstawienie obiektów typu A do listy, ale to jest poza punktem) w moim oryginalnym przykładzie kod nadal nie powinien kopiować ani przenosić żadnych obiekty i jako takie powodują błędy kompilacji:

#include <vector> 
#include <list> 
#include <memory> 
struct A 
{ 
    std::vector<std::unique_ptr<int>> v; 
    A(A&&) = delete; 
    A(const A&) = delete; 
}; 
int main() 
{ 
    std::list<A> l; 
    l.sort([](const A& a1, const A& a2){ return true; }); 
} 
4

Jest to "problem z VC", ale tylko dlatego, że jesteś nadużywania Visual Studio.

VC++ implementuje referencje wartością r, ale robi , a nie zaimplementować generatory ruchu/operatory przypisania generowane przez kompilator. Co oznacza, że ​​jeśli chcesz, aby dany typ był ruchomy, musisz napisać go samemu.

A nie jest typem ruchomym, więc różne funkcje std::list spróbują je skopiować. I zawiodą, gdy spróbują skopiować vector z unique_ptr. Stąd błąd kompilatora.

Jeśli potrzebujesz obiektów poruszających się w VC++, musisz napisać dla nich samodzielnie konstruktory/przypisania.

+2

Dlaczego sortowanie listy (a nie wektor) wymaga skopiowania czegokolwiek? Czy sortowanie nie powinno być realizowane poprzez zmianę "poprzednich" i "następnych" wskaźników podwójnie połączonych węzłów list? – PowerGamer

3

Problem jest naprawdę w VC11, jako it doesn't implement C++11 feature of automatically generating move operations (jak już przybity przez Nicola Bolasa).

Poniższy kod jest kompilowany z VC10 SP1; w tym kodzie przykładowym konstruktorem przenoszenia jest jawnie napisany (zamiast tego używany jest ruch operator=, używany jest copy-and-swap idiom).

#include <algorithm> // for std::swap (for copy-and-swap idiom) 
#include <list> 
#include <memory> 
#include <vector> 

struct A 
{ 
    std::vector<std::unique_ptr<int>> v; 

    A(A&& other) 
     : v(std::move(other.v)) 
    { 
    } 

    A& operator=(A other) 
    { 
     swap(*this, other); 
     return *this; 
    } 

    friend void swap(A& lhs, A& rhs) 
    { 
     using std::swap; 
     swap(lhs.v, rhs.v); 
    } 
}; 

int main() 
{ 
    std::list<A> l; 
    l.sort([](const A& , const A&){ return true; }); 
} 
+1

Jestem w pełni świadomy tego, co jest konstruktorem ruchu i faktem, że VC11 nie generuje ich niejawnie. To nie wyjaśnia w najmniejszym przypadku DLACZEGO VC11 chce skopiować lub przenieść obiekt typu A podczas sortowania listy *. – PowerGamer

+0

Cóż, możesz znaleźć odpowiedź w kodzie źródłowym STL. Rozumiem twój punkt widzenia. –

Powiązane problemy