ベクトルが大きくなったときに移動セマンティクスを強制する方法は?


92

std::vectorあるクラスのオブジェクトを持っていますA。クラスは自明ではなく、コピーコンストラクター移動コンストラクターが定義されています。

std::vector<A>  myvec;

ベクターをAオブジェクトで(たとえばを使用してmyvec.push_back(a))埋めると、コピーコンストラクターA( const A&)を使用してベクターの要素の新しいコピーをインスタンス化し、ベクターのサイズが大きくなります。

A代わりに、クラスの移動コンストラクタが代わりに使用されていることを強制できますか?


5
移動対応のベクトル実装を使用することにより、これを行うことができます。
K-ballo

2
これを達成する方法をもう少し具体的に教えていただけますか?
Bertwim van Beest

1
移動対応のベクトル実装を使用するだけです。標準ライブラリの実装(それは何ですか?)は移動に対応していないようです。Boostのmove-awareコンテナーを試すことができます。
K-ballo

1
さて、私は移動に対応しているgcc 4.5.1を使用しています。
Bertwim van Beest

私のコードでは、移動コンストラクターに明示的な「noexcept」がなかったとしても、コピーコンストラクターをプライベートにすることができました。
Arne、

回答:


126

C ++に(具体的にはstd::vector)を使用して、移動コンストラクタとデストラクタがスローしないことを通知する必要がありますnoexcept。次に、ベクトルが大きくなると、移動コンストラクターが呼び出されます。

これは、以下によって尊重される移動コンストラクタを宣言および実装する方法ですstd::vector

A(A && rhs) noexcept { 
  std::cout << "i am the move constr" <<std::endl;
  ... some code doing the move ...  
  m_value=std::move(rhs.m_value) ; // etc...
}

コンストラクタがない場合にはnoexceptstd::vectorそれが標準で要求される例外保証を確保することができないので、それを使用することはできません。

標準での発言の詳細については、C ++ Moveセマンティクスと例外をご覧ください。

それが例外と関係があるかもしれないことをほのめかしたボーへの信用。またemplace_back、可能な場合はKerrek SBのアドバイスを考慮して使用してください。これより高速になる可能性がありますが(多くの場合はそうではありません)、より明確でコンパクトになる可能性がありますが、いくつかの落とし穴もあります(特に非明示的なコンストラクターの場合)。

編集、多くの場合、デフォルトはあなたが望むものです:移動可能なすべてのものを移動し、残りをコピーします。それを明示的に求めるには、

A(A && rhs) = default;

そうすることで、可能な場合はnoexceptが得られます。デフォルトのMoveコンストラクターはnoexceptとして定義されていますか?

Visual Studio 2015以前の以前のバージョンでは、移動のセマンティクスはサポートされていますが、これはサポートされていません。


関心のうち、どのようにない独自の実装は、かどうかを「知っている」value_typeの動きctorのですかnoexcept?おそらく、呼び出しスコープもnoexcept関数である場合、言語は関数呼び出し候補セットを制限しますか?
オービットのライトネスレース2013年

1
@LightnessRacesinOrbit en.cppreference.com/w/cpp/types/is_move_constructibleのようなものを実行しているだけだと思います。moveコンストラクターは1つしか存在できないため、宣言によって明確に定義する必要があります。
Johan Lundberg、2013

@LightnessRacesinOrbit、私はそれ以来、noexcept移動コンストラクタがあるかどうかを本当に知る(標準的で便利な)方法がないことを学びました。コピーコンストラクタis_nothrow_move_constructibleがある場合はtrueになりますnothrow。高価なnothrowコピーコンストラクターの実際のケースについては知りません。
Johan Lundberg

うまくいきません。解体子、移動コンストラクター、および移動割り当て関数はすべてnoexceptヘッダーと実装の両方でマークされており、push_back(std:; move)を実行しても、コピーコンストラクターが呼び出されます。ここで髪をちぎりました。
AlastairG

1
@ヨハン私は問題を見つけました。std::move()間違ったpush_back()通話で使用していました。あなたが目の前に明らかなエラーを見ないほど問題を探しているときの1つ。そして、それはランチタイムで、コメントを削除するのを忘れました。
AlastairG

17

興味深いことに、gcc 4.7.2のベクターは、移動コンストラクターとデストラクターの両方がである場合にのみ、移動コンストラクターを使用しますnoexcept。簡単な例:

struct foo {
    foo() {}
    foo( const foo & ) noexcept { std::cout << "copy\n"; }
    foo( foo && ) noexcept { std::cout << "move\n"; }
    ~foo() noexcept {}
};

int main() {
    std::vector< foo > v;
    for ( int i = 0; i < 3; ++i ) v.emplace_back();
}

これは期待される出力です:

move
move
move

ただし、noexceptから削除する~foo()と、結果が異なります。

copy
copy
copy

これもこの質問の答えだと思います。


他の回答は移動コンストラクタについてのみ話しているように見えますが、デストラクタが例外である必要はありません。
Nikola Benes 2013年

まあ、そうであるべきですが、結局のところ、gcc 4.7.2ではそうではありませんでした。したがって、この問題は実際にはgccに固有のものでした。ただし、gcc 4.8.0で修正される予定です。関連するStackoverflowの質問を参照してください。
ニコラベネス2013年

-1

std::vector再割り当てに使用移動セマンティクスを強制する唯一の方法(C ++ 17以前の場合)は、コピーコンストラクターを削除することです:)。このようにして、コンパイル時にmoveコンストラクターを使用するか、試行錯誤します:)。

std::vector再配置時にmoveコンストラクターを使用してはならないルールはたくさんありますが、それをどこで使用なければならないかについては何もありません

template<class T>
class move_only : public T{
public:
   move_only(){}
   move_only(const move_only&) = delete;
   move_only(move_only&&) noexcept {};
   ~move_only() noexcept {};

   using T::T;   
};

住む

または

template<class T>
struct move_only{
   T value;

   template<class Arg, class ...Args, typename = std::enable_if_t<
            !std::is_same_v<move_only<T>&&, Arg >
            && !std::is_same_v<const move_only<T>&, Arg >
    >>
   move_only(Arg&& arg, Args&&... args)
      :value(std::forward<Arg>(arg), std::forward<Args>(args)...)
   {}

   move_only(){}
   move_only(const move_only&) = delete;   
   move_only(move_only&& other) noexcept : value(std::move(other.value)) {};    
   ~move_only() noexcept {};   
};

ライブコード

あなたのTクラスは、持っている必要がありますnoexcept移動コンストラクタ/ assigmentオペレータとnoexceptデストラクタを。そうしないと、コンパイルエラーが発生します。

std::vector<move_only<MyClass>> vec;

1
コピーコンストラクタを削除する必要はありません。moveコンストラクターがnoexceptの場合、それが使用されます。
Balki

@balki使用される場合があります。現在、Standardはこれを要求していません。これは、groups.google.com
a
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.