2012-12-10 10 views
8

pojawiają się błędy kompilacji na g++ (GCC) 4.7.2 ale nie na MSVC-2012 podczas próby std::vector::push_back niebędącego copyable (prywatny konstruktora kopia), ale ruchomego obiektu. Dla mnie mój przykład wygląda identycznie jak wiele innych przykładów na SO i gdzie indziej. Komunikat o błędzie sprawia, że ​​wygląda na to, że struktura nie jest "bezpośrednia, możliwa do skonstruowania" - nie wiem, co to znaczy, dlatego nie mam wątpliwości co do tego, dlaczego obiekt musi być "możliwy do bezpośredniej konstrukcji", aby można go było odepchnąć.std :: vector :: push_back non-copyable obiekt daje błąd kompilatora

#include <vector> 
#include <memory> 

struct MyStruct 
{ 

    MyStruct(std::unique_ptr<int> p); 
    MyStruct(MyStruct&& other); 
    MyStruct& operator=(MyStruct&& other); 

    std::unique_ptr<int> mP; 

private: 
      // Non-copyable 
    MyStruct(const MyStruct&); 
    MyStruct& operator=(const MyStruct& other); 
}; 

int main() 
{ 

    MyStruct s(std::unique_ptr<int>(new int(5))); 
    std::vector<MyStruct> v; 

    auto other = std::move(s);  // Test it is moveable 
    v.push_back(std::move(other)); // Fails to compile 

    return 0; 
} 

Daje błędom

/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/type_traits: In instantiation of ‘struct std::__is_direct_constructible_impl<MyStruct, const MyStruct&>’: 
... snip ... 
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_vector.h:900:9: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = MyStruct; _Alloc = std::allocator<MyStruct>; std::vector<_Tp, _Alloc>::value_type = MyStruct]’ 
main.cpp:27:33: required from here 
main.cpp:16:5: error: ‘MyStruct::MyStruct(const MyStruct&)’ is private 

Proste obejście z różnych odpowiedzi:

  • Zastosowanie MyStruct(const MyStruct&) = delete; zamiast private ctor włamywanie
  • Dziedziczy boost::noncopyable (lub innej klasy z prywatnym ctor)
+0

Ten kod powinien rzeczywiście kompilować idealnie dobrze, brzmi jak jakiś dziwny problem SFINAE. Czy możesz wypróbować '.emplace_back (std :: move (other))'? – Xeo

+0

Identyczny błąd z 'emplace_back' (próbowałem już tego - wraz z kilkoma inkantacjami;) – Zero

+0

Co się stanie, jeśli spróbujesz po prostu' std :: unique_ptr'? – Xeo

Odpowiedz

13

Niepowodzenie to spowodowane ograniczeniem G ++ 4.7, który nie wdrożyć DR 1170, który został zmieniony bardzo późno w C++ 11 procesie normalizacji powiedzieć, że kontrola dostępu powinny być wykonane jako część szablonu odliczenia argumentów .

Podstawową przyczyną jest to, że libstdc ruszy ++ 's vector elementy, jeżeli zapewniona jest operacja ruch, aby nie rzucać (czyli jest zadeklarowana noexcept lub throw()), w przeciwnym razie, jeśli typ jest copyable elementy zostaną skopiowane, w przeciwnym razie, jeśli typ nie jest możliwe do skopiowania, ale ma operację przenoszenia z możliwością rzucania, a następnie zostanie przeniesiony (a jeśli zostanie zgłoszony wyjątek, wyniki nie zostaną określone). Jest to realizowane za pomocą sprawdzania cech typu is_nothrow_move_constructible i is_copy_constructible. W twoim przypadku typ nie jest ruchomy, a cecha is_copy_constructible jest zaznaczona. Twój typ ma konstruktor kopiowania, ale nie jest dostępny, więc cecha is_copy_constructible powoduje błąd kompilacji w G ++ 4.7, ponieważ sprawdzanie dostępu nie odbywa się podczas dedukcji argumentu szablonu.

Jeśli dokonać konstruktora poruszać i przemieszczać operator przypisania noexcept wówczas typ zostanie przeniesiony i nie musi być copyable, więc is_copy_constructible cecha, że ​​nie nie jest używany, a kod kompiluje OK.

Ewentualnie, (jak również stwierdzono w komentarzach), jeśli usuniesz konstruktora kopiowania, wówczas właściwość is_copy_constructible uzyskuje właściwy wynik.

Inną alternatywą jest użycie coś jak boost::noncopyable które pośrednio sprawia konstruktora kopii usuniętej więc cecha is_copy_constructible działa prawidłowo (a także współpracuje ze starszymi kompilatorów jak MSVC, które nie obsługują funkcji skasowanych prawidłowo). Nie wiem, co masz na myśli mówiąc, że nie można znaleźć błędu, czy MSVC nie pokazuje pełnego kontekstu błędu kompilatora?

Wnioski: Stosowanie unique_ptr gdzie właściwe, ale nie robią klas wyraźnie ruchome

Nie zgadzam się z tym wnioskiem, jest zbyt ekstremalne. Zamiast tego spraw, aby lekcje były nieruchome, gdy tylko jest to możliwe. Jeśli to możliwe, użyj usuniętych funkcji, aby utworzyć typ niekopiowalny zamiast prywatnych + niezaimplementowanych funkcji, może używając makra do przenoszenia do starszych kompilatorów, np.

#if __cplusplus >= 201103L 
#define NONCOPYABLE(TYPE) \ 
    TYPE(const TYPE&) = delete; TYPE& operator=(const TYPE&) = delete 
#else 
// must be used in private access region 
#define NONCOPYABLE(TYPE) \ 
    TYPE(const TYPE&); TYPE& operator=(const TYPE&) 
#endif 

struct MyStruct 
{ 
... 
private: 
    NONCOPYABLE(MyStruct); 
}; 
+0

Aby wyjaśnić: czy oryginalne standardy kodowe są zgodne, czy nie? Podsumowując - odniosłem się bardziej do standardów kodowania firm (z którymi nie zgadzam się!). Żonglowanie zgodnością MSVC i gcc, zapewnia równowagę między stylem, który unika tych "niespodzianek", trudnych do rozwiązania komunikatów o błędach (od 'boost :: noncopyable') i zadowalającej wydajności. W szczególności, dopóki MSVC nie wygeneruje kompilatora ruchu zgodnie z najnowszym standardem, jest to zbyt dużo bojlerów dla zbyt małej korzyści. – Zero

+0

Kod jest prawidłowy, jest odrzucony, ponieważ G ++ 4.7 nie może zaimplementować zgodnego z normą 'is_copy_constructible' z powodu braku sprawdzenia dostępu podczas dedukcji argumentu szablonu (tj. Nie implementuje [DR 1170] (http: //www.open-std .org/jtc1/sc22/wg21/docs/cwg_defects.html # 1170)). Kod działa z G ++ 4.8 lub nowszymi wersjami Clang ++, ponieważ implementują DR 1170 –

+0

, DR1170 zmienia się całkowicie. Mamy wiele typowych cech, które zawodzą w wyniku prywatnych deklarowanych treści. Jest to zawsze wspomniane jako ograniczenie funkcji w dokumentach. Wydaje się teraz, że po DR1170, cechy nie będą miały ograniczeń z powodu problemów z dostępem? Przykład: http://www.boost.org/doc/libs/1_58_0/libs/type_traits/doc/html/boost_typetraits/reference/has_bit_or.html akapit 'Znane problemy' ** Ta cecha nie może wykryć, czy operator binarny | jest publiczne lub nie: jeśli operator | jest zdefiniowany jako prywatny element Lhs, a następnie utworzenie wystąpienia is_bit_or lub spowoduje błąd kompilatora. ** –

Powiązane problemy