2017-11-04 99 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可以接受右值引用,因此在此情況下將使用該值,並且不會出現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; 
^

我瞭解一些關於什麼可以移動和什麼不能移動的規則,但是這似乎並不重要。爲什麼會發生這種情況,如何解決這個問題,所以它不會調用複製構造函數?

目標編譯器是Clang C++ 14。

+0

我希望'聯盟中的std :: string'只是例如 –

+1

你打算如何確定這個聯盟的某個任意實例(來自某個未指定的源)是否包含構造的'std :: string',可以移動的,還是其他原始類型之一?如果給定的聯合實際上包含'bool'值,試圖移動它所包含的'std :: string'會導致無盡的歡樂。如果你考慮一下,你應該能夠回答你自己的問題。 –

回答

2

它試圖調用您的拷貝構造函數,因爲您的移動構造函數是已刪除。哦,當然,我知道你寫了= default。但是由於聯合包含一個帶有不平凡的移動/複製構造函數的類型,如果聯合的複製/移動構造函數不是用戶提供的,它將被隱式刪除。

= default不會使其成爲「用戶提供」。

換句話說,如果編譯器的任何成員需要複製/移動memcpy(又名:不可複製的)代碼以外的其他代碼,複製/移動構造函數編譯器不能給union。因此,包含聯合的類型不能具有由編譯器生成的複製/移動代碼。在這些情況下,您必須決定如何複製/移動對象。

而這樣的複製/移動構造函數需要知道它是哪種類型。

相關問題