2017-11-04 64 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。因此,包含联合的类型不能具有由编译器生成的复制/移动代码。在这些情况下,您必须决定如何复制/移动对象。

而这样的复制/移动构造函数需要知道它是哪种类型。

相关问题