10

コピー不可 (プライベート コピー コンストラクター) で移動可能なオブジェクトを使用しようとすると、コンパイル エラーg++ (GCC) 4.7.2が発生しますが、発生しません。私にとって、私の例は、SO や他の場所の他の多くの例と同じに見えます。エラーメッセージは、構造体が「直接構築可能」ではないという問題のように見えます-これが何を意味するのかわからないので、オブジェクトをプッシュバックするために「直接構築可能」である必要がある理由について二重に確信が持てません。MSVC-2012std::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

さまざまな回答からの簡単な回避策:

  • ハッキングMyStruct(const MyStruct&) = delete;の代わりに使用private ctor
  • 継承boost::noncopyable(またはプライベート ctor を持つ別のクラス)
4

1 に答える 1

15

この失敗は、 DR 1170を実装していない G++ 4.7 の制限によるものです。DR 1170は、C++11 標準化プロセスの非常に遅い段階で変更され、テンプレート引数推定の一部としてアクセス チェックを実行する必要があると述べています。

vector根本的な原因は、移動操作がスローされないことが保証されている場合 (つまり、noexceptorが宣言されている場合throw())、libstdc++が要素を移動することです。移動操作をスローすると移動されます (例外がスローされた場合、結果はundefined unspecifiedis_nothrow_move_constructibleになります) is_copy_constructible。あなたの場合、型は構築可能ではないため、is_copy_constructible特性がチェックされます。型にはコピー コンストラクターがありますが、アクセスできません。そのため、is_copy_constructibleテンプレート引数推定中にアクセス チェックが行われないため、トレイトは G++ 4.7 でコンパイラ エラーを生成します。

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

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

もう 1 つの方法は、boost::noncopyable暗黙的にコピー コンストラクターを削除するようなものを使用して、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);
};
于 2012-12-10T22:50:30.803 に答える