7

インターネットでどこを読んでも、自分のクラスをうまく機能させたい場合std::vector(つまり、自分のクラスのムーブ セマンティクスが によって使用された場合std::vector) は、ムーブ コンストラクターを 'noexcept' (またはnoexcept(true)) として宣言する必要があることを強くお勧めします。

実験としてstd::vectorマークしたのに、なぜそれを使用したのですか?noexcept(false)

#include <iostream>
#include <vector>
using std::cout;

struct T
{
    T() { cout <<"T()\n"; }

    T(const T&) { cout <<"T(const T&)\n"; }

    T& operator= (const T&)
    { cout <<"T& operator= (const T&)\n"; return *this; }

    ~T() { cout << "~T()\n"; }

    T& operator=(T&&) noexcept(false)
    { cout <<"T& operator=(T&&)\n"; return *this; }

    T(T&&) noexcept(false)
    { cout << "T(T&&)\n"; }
};

int main()
{
    std::vector<T> t_vec;
    t_vec.push_back(T());
}

出力:

T()
T(T&&)
~T()
~T()

なんで ?私は何を間違えましたか?

CXX_FLAGS を次のように設定して gcc 4.8.2 でコンパイル:

--std=c++11 -O0 -fno-elide-constructors
4

2 に答える 2

12

あなたは何も悪いことをしていません。

move-ctor のスローを回避する必要があると誤って考えpush_backていました。少なくとも新しい要素を構築する場合はそうではありません。

move-ctors / move-assignments のスローを避ける必要がある唯一の場所は、要素の半分が移動され、残りが元の場所に移動することを避けるために、ベクトルの再割り当て時です。

この関数には、強力な例外安全性が保証されています。

操作が成功するか、失敗して何も変更されていません。

于 2014-10-06T20:19:06.073 に答える
4

vector::push_backストレージを再割り当てする必要がある場合は、最初に新しいメモリを割り当ててから、move によって新しい要素が最後の位置に構築されます。それがスローされ、新しいメモリの割り当てが解除され、何も変更されていない場合、移動コンストラクターがスローできる場合でも、強力な例外安全性が保証されます。

スローされない場合、既存の要素は元のストレージから新しいストレージに転送されます。ここnoexceptで、ムーブ コンストラクターの仕様が重要になります。移動する可能性があり、タイプが CopyConstructible の場合、既存の要素は移動される代わりにコピーされます。

ただし、テストでは、新しい要素がベクターにどのように挿入されるかを確認するだけであり、そのステップでスロー コンストラクターを使用することは常に問題ありません。

于 2014-10-07T11:01:26.773 に答える