2012-12-10 26 views
8

std::vector::push_backコピー不可能(プライベートコピーコンストラクタ)で移動可能なオブジェクトにしようとすると、g++ (GCC) 4.7.2でコンパイルエラーが発生しますが、MSVC-2012ではコンパイルエラーが発生します。私の例は、SOや他の多くの例と同じように見えます。エラーメッセージは、構造体が「直接構築可能」ではないという問題のように見えます。なぜこれが何を意味するのかわからないので、なぜオブジェクトをプッシュバックする必要があるのか​​が不明です。std :: vector :: push_backコピー不可能なオブジェクトでコンパイルエラーが発生する

#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; 
} 

エラーに様々な答えから

/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 

簡単な回避策与える:

  • 継承boost::noncopyable(またはプライベートctorのと別のクラス)をハック

    • 使用MyStruct(const MyStruct&) = delete;代わりprivate ctorのを
  • +0

    そのコードは実際には奇妙なSFINAE問題のような完璧なコンパイルが必要です。あなたは '.emplace_back(std :: move(other))'を試すことができますか? – Xeo

    +0

    'emplace_back'と同じエラーです(私はすでにそれを試していましたが、いくつかの要点と一緒に) – Zero

    +0

    ' std :: unique_ptr'だけを実行するとどうなりますか? – Xeo

    答えて

    13

    エラーは、DR 1170を実装していないG ++ 4.7の制限によるものであり、C++ 11標準化プロセスで非常に遅れて変更され、テンプレート引き数減算の一部としてアクセス検査を行う必要があると言われています。

    根本的な原因は、タイプがコピー可能であればそれ以外の要素はタイプではないそうでない場合は、コピーされます、移動操作がスローしないことが保証されている場合にlibstdC++のvector(すなわち、それがnoexceptまたはthrow()を宣言しています)の要素を移動することです例外がスローされた場合、結果は 未定義 未指定です。これはis_nothrow_move_constructibleis_copy_constructible型の特性にチェックされて実装されています。あなたのケースでは、型は移動可能ではありませんので、is_copy_constructibleの特性がチェックされます。型にコピーコンストラクタがありますがアクセス不可能なので、テンプレート引数の控除中にアクセスチェックが行われないため、is_copy_constructibleの特性によってG ++ 4.7でコンパイラエラーが発生します。

    移動コンストラクタを作成して代入演算子noexceptを移動した場合、その型は移動され、コピー可能である必要はないため、is_copy_constructibleの特性が使用されず、OKのコードがコンパイルされます。

    また、コピーコンストラクタを削除した場合は、is_copy_constructibleの特性が正しい結果を得ます(コメントにも記載されています)。

    もう1つの方法は、is_copy_constructible特性が正しく機能するように暗黙的にコピーコンストラクタを削除するようなものを使用することです(削除された関数を適切にサポートしていないMSVCなどの古いコンパイラでも機能します)。私はあなたがエラーを見つけることが不可能にすることについてあなたが意味するものを知らない、MSVCはあなたにコンパイラエラーの完全なコンテキストを表示していないのですか?

    結論:適切なunique_ptrを使用しますが、私はこの結論に反対

    クラスが明示的に移動しないでください、それはあまりにも極端です。代わりに、できるだけクラスを移動しないでください。また、可能であれば、削除された関数を使用して、private + unimplemented関数の代わりにコピー不可能な型を作成します。おそらく古いコンパイラへの移植性のためにマクロを使用します。

    #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

    明確にする:元のコード標準は準拠しているかどうか? 結論 - 私は、会社の標準をコーディングしていることに言及していました(それには不一致があります!)。 MSVCとgccのコンプライアンスを酷使すると、これらの「驚き」を避けるスタイルと、エラーメッセージ(「boost :: noncopyable」)と満足のいくパフォーマンスを解決するのが難しくなります。特に、MSVCが最新の標準に沿ってコンパイラ生成の移動体を生成するまでは、あまりにも有益ではありません。 – Zero

    +0

    GML 4.7はテンプレート引数の控除中にアクセスをチェックしないため、標準準拠の 'is_copy_constructible'を実装できないため、コードは有効です。拒否されました(つまり、[DR 1170](http://www.open-std .org/jtc1/sc22/wg21/docs/cwg_defects.html#1170))。 DR 1170 –

    +0

    を実装しているので、このコードはG ++ 4.8またはClang ++の最新バージョンで動作します。このDR1170は完全にゲームを変更しています。私たちは多くのyyyy型の特性を持っていますが、それは私的に宣言されたもののために失敗します。これは、常に文書の特性限界として言及されています。 DR1170の後、アクセスの問題により特性に制限がないようになったようです。例:http://www.boost.org/doc/libs/1_58_0/libs/type_traits/doc/html/boost_typetraits/reference/has_bit_or.html段落「既知の問題」**この特性は、バイナリ演算子| publicかどうか:if演算子|がLhのプライベートメンバーとして定義されている場合、has_bit_orまたはをインスタンス化するとコンパイルエラーが発生します** –

    関連する問題