web-dev-qa-db-ja.com

コピーコンストラクタなしでオブジェクトを持つベクトルに挿入します

コピーコンストラクターが明示的に削除されているクラスがあります(Aは内部でポインターを使用し、浅いコピーの落とし穴に陥りたくないため):

class A {
  public:
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    A(const B& b, const C& c);
}

これで、タイプvector<A> aVector;のベクトルがあり、それに要素を挿入したいので、emplace_backを使用します。

aVector.emplace_back(b, c);

ただし、これはgccを使用したコンパイルに失敗し、エラーが発生します-

third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...)
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:77:3:   required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator)  
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:119:41:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) 
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:260:63:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) 
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/stl_uninitialized.h:283:67:   required from '_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&)
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/vector.tcc:410:6:   required from 'void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) 
third-party/gcc-4.7.1-glibc-2.14.1/libgcc/libgcc-4.7.1/afc21dc/include/c++/4.7.1/bits/vector.tcc:102:4:   required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...)

このエラーの理由は何ですか?また、コピーコンストラクターの削除を削除せずに修正するにはどうすればよいですか?移動コンストラクターが必要ですか?明示的に定義する必要がありますか?

17
vigs1990

_std::vector::emplace_back_は、コピー/移動コンストラクターを必要とする再配置を行う可能性があるため、移動コンストラクターを追加する必要があります。または、_std::deque_を使用します。

ライブデモ

_#include <vector>
#include <deque>
using namespace std;

struct NoCopyNoMove
{
    NoCopyNoMove(const NoCopyNoMove&) = delete;
    NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
    NoCopyNoMove(NoCopyNoMove&&) = delete;
    NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;

    NoCopyNoMove(int){};
};

struct OnlyMove
{
    OnlyMove(const OnlyMove&) = delete;
    OnlyMove& operator=(const OnlyMove&) = delete;
    OnlyMove(OnlyMove&&) noexcept {}
    OnlyMove& operator=(OnlyMove&&) noexcept {}

    OnlyMove(int){};
};

int main()
{
    deque<NoCopyNoMove> x;
    x.emplace_back(1);

    vector<OnlyMove> y;
    y.emplace_back(1);
}
_

§23.2.3表101—オプションのシーケンスコンテナ操作

a.emplace_back(args) [...]

RequiresTは、引数からEmplaceConstructibleXになります。 vectorの場合、TMoveInsertable into Xになります。

22
Evgeny Panasyuk

エラーはemplace_backのせいではありません。オブジェクトをベクトルに配置するには、オブジェクトが移動可能またはコピー可能である必要があります。コピーコンストラクターを実装して実際にコードを実行すると、呼び出されないことがわかります。これはcppreference.comのエントリです
enter image description here

これを修正するために私がすることは、moveコンストラクターを実装することです。これにより、moveコンストラクターがコンパイルされ、moveコンストラクターを持つことの実際の欠点はわかりません。また、cctorと同様に、moveコンストラクターは現在のコードでは呼び出されません。

10
aaronman

私は外部ライブラリのクラスでこの問題に遭遇しました。私は得ていた、

"Error C2280 ClassName::ClassName(const ClassName &)': attempting to reference a deleted function"

私が使用していたクラスにはdeletedコピーコンストラクターがあったと思います。カスタム派生クラスオブジェクト用に知っていたstdコンテナに追加できませんでした。このオブジェクトは、初期化/エラーチェックを支援するために私のヘルパーでオブジェクトをラップしていました。

私はこのブロッカーを(risky)ポインターで回避しました。

基本的に、私はこれに移行しました:

std::vector<ClassName*> names;
ClassName name("arg");
ClassName name_ptr = &name;
names.Push_back(name_ptr);

これから、もともと:

std::vector<ClassName> names;
ClassName name("arg");
names.Push_back(name);

興味深いことに、これは私が実際にC++でコーディングするのは初めてでした必要既知の代替手段がないため、ポインター固有ではない使用要件にポインターを使用します。そのため、自分のコード内で基本的な何かを見逃しているのではないかと心配しています。

これを行うためのより良い方法があるかもしれませんが、それはまだこの質問の回答リストにありません...

警告の編集:

私は前にこれについて言及するべきでした、ありがとう aschepler ;これを実行し、使用しているコンテナがオブジェクトよりも長生きすると、 "bang、you're dead。"

1

@ kayleeFrye_onDeckの回答 に追加したいだけです。私は彼らとほぼ同じ状況にあり、(コメントのフィードバックに基づいて)私に役立つ正確な構文は次のとおりです。

vector< std::unique_ptr<ClassName> > names; // Declare vector of unique_ptrs of the class instance

std::unique_ptr<ClassName> name_ptr = std::make_unique<ClassName>();
names.Push_back(std::move(name_ptr)); // Need to use std::move()

// Now you can access names objects without error:
names[0]->classMethod();
1
A__