2017-11-04 2 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; 
^

I何が動くことができ、何ができないかに関するいくつかのルールについて知っていますが、これは扱いが簡単ではないようです。なぜこれが起こっているのですか?これを解決してコピーコンストラクタを呼び出さないようにするにはどうすればいいですか?

ターゲットコンパイラはClang C++ 14です。

+0

ユニオンの 'std :: string'がちょうど例えば –

+1

であることを希望します。このユニオンの任意のインスタンス(不特定のソースから来たもの)に構築された' std :: string'が含まれているかどうかを、移動することができる、または他のプリミティブ型の1つですか?与えられた共用体が実際に 'bool'値を含んでいれば、' std :: string'に含まれていると思われるものを移動しようとすると無限の喜びが生じます。あなたがそれについて考えるなら、しばらくの間、自分の質問に答えることができるはずです。 –

答えて

2

移動コンストラクタがであるため、コピーコンストラクタを呼び出そうとしていますが削除されました。ああ、確かに、私はあなたが= defaultを書いたことを知っています。しかし、unionには単純なmove/copyコンストラクタを持つ型が含まれているので、unionのコピー/移動コンストラクタはユーザが提供していなければ暗黙的に削除されます。

= defaultは、「ユーザー提供」にはなりません。

別の言い方をすれば、memcpy(別名コピー可能ではない)のコピー/移動コードを必要とするメンバーなら、コピー/移動コンストラクタをunionに与えることはできません。したがって、共用体を含む型はコンパイラ生成のコピー/移動コードを持つことはできません。このような場合は、オブジェクトをコピー/移動する方法を決定する必要があります。

このようなコピー/移動コンストラクタは、どのタイプであるかを知る必要があります。

関連する問題