2017-11-04 18 views
0

나는 move이 아닌 객체에 적용 할 수 있음을 알았습니다.에는 "평범하지 않습니다"(예 : 기본 유형은 괜찮습니다) 멤버가있는 공용체가 있습니다. 예를 들어, 다음 코드는 (C++ 14, Clang)을 컴파일합니다."이동 된"객체가 공용체에 "평범하지 않은"멤버를 가질 때 복사 생성자가 강제되는 이유는 무엇입니까?

#include <vector> 
#include <string> 

class Foo { 
public: 
    union { 
    int i; 
    bool b; 
    }; 

    Foo() {}; 
    ~Foo() {}; 

    // Move constructor to default. 
    Foo(Foo &&) = default; 

    // Copy constructor deleted. 
    Foo(const Foo &) = delete; 
}; 

int main() { 
    std::vector<Foo> v; 
    v.push_back(Foo()); 
} 

복사 생성자가 삭제됩니다. std::vectorpush_back은 rvalue 참조를 허용 할 수 있으므로이 경우이 값을 사용하며 copy이 발생하지 않습니다. 는 "이 아닌 사소한"유형이 노동 조합에 추가됩니다 그러나 일단 복사 생성자는 강제로 - 그래서 컴파일되지 않습니다 :

#include <vector> 
#include <string> 

class Foo { 
public: 
    union { 
    int i; 
    bool b; 
    std::string s; // <-- Added element causing compile error. 
    }; 

    Foo() {}; 
    ~Foo() {}; 

    // Move constructor to default. 
    Foo(Foo &&) = default; 

    // Copy constructor deleted. 
    Foo(const Foo &) = delete; 
}; 

int main() { 
    std::vector<Foo> v; 
    v.push_back(Foo()); 
} 

컴파일러 오류 메시지의 관련 부분 :

In file included from experiment/miniso.cpp:1: 
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/vector:61: 
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/allocator.h:46: 
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/x86_64-linux-gnu/c++/7.2.0/bits/c++allocator.h:33: 
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/ext/new_allocator.h:136:23: error: call to deleted constructor of 'Foo' 
     { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } 
          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/alloc_traits.h:475:8: note: in instantiation of function template 
     specialization '__gnu_cxx::new_allocator<Foo>::construct<Foo, Foo>' requested here 
     { __a.construct(__p, std::forward<_Args>(__args)...); } 
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/vector.tcc:100:21: note: in instantiation of function template 
     specialization 'std::allocator_traits<std::allocator<Foo> >::construct<Foo, Foo>' requested here 
      _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, 
         ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/stl_vector.h:954:9: note: in instantiation of function template 
     specialization 'std::vector<Foo, std::allocator<Foo> >::emplace_back<Foo>' requested here 
     { emplace_back(std::move(__x)); } 
     ^
experiment/miniso.cpp:24:5: note: in instantiation of member function 'std::vector<Foo, std::allocator<Foo> >::push_back' requested here 
    v.push_back(Foo()); 
    ^
experiment/miniso.cpp:19:3: note: 'Foo' has been explicitly marked deleted here 
    Foo(const Foo &) = delete; 
^

I 무엇이 움직일 수 있는지, 할 수 없는지에 관한 몇 가지 규칙을 알고 있지만, 다루기는 쉽지 않습니다. 왜 이런 일이 일어나고 이것이 어떻게 해결되어 복사 생성자를 호출하지 않을 수 있습니까?

대상 컴파일러는 Clang C++입니다.

+0

유니온의 std :: string은 (예 : –

+1

) 희망합니다.이 union의 임의의 인스턴스 (지정되지 않은 소스에서 온 것)가 생성 된'std :: string'을 포함하는지 여부를 어떻게 결정합니까? 움직일 수있는, 또는 다른 원시 타입 중 하나? 주어진 유니온이 실제로'bool' 값을 포함하고 있다면'std :: string'에 포함 된 것으로 추정되는 것들을 이동 시키려고하면 무한한 우스꽝스러운 결과가 발생합니다. 생각해 보면 잠시 동안 자신의 질문에 대답 할 수 있어야합니다. –

답변

2

이동 생성자가 이 (가)으로 삭제되었으므로 복사 생성자를 호출하려고합니다. 오, 물론, 당신이 = default을 썼다는 것을 알고 있습니다. 그러나 공용체에는 이동/복사 생성자가 포함 된 형식이 포함되어 있으므로 사용자가 제공하지 않으면 공용 구조체의 복사/이동 생성자가 암시 적으로 삭제됩니다.

그리고 = default은 "사용자 제공"하지 않습니다.

다시 말해서 memcpy (일명 복사 가능하지 않음) 이외의 코드를 복사/이동해야하는 컴파일러의 경우 복사/이동 생성자에 union을 제공 할 수 없습니다. 따라서 공용체를 포함하는 형식에는 컴파일러에서 생성 된 복사/이동 코드도 포함될 수 없습니다. 이러한 경우 개체를 복사/이동하는 방법을 결정해야합니다.

그런 복사/이동 생성자는 어떤 유형인지 알아야합니다.