9

私の理解では、たとえばベクター内で再割り当てするときに、コンパイラーがそれらを利用できるようにするには、move-constructors と move-assign を noexcept とマークする必要があります。

しかし、move-assign、move-construct が実際にスローする可能性がある実際のケースはありますか?

更新

たとえば、構築時にリソースが割り当てられているクラスは、非スロー移動できません。

4

3 に答える 3

9

しかし、move-assign、move-construct (または swap) が実際にスローする可能性がある実際のケースはありますか?

はい。の実装を考えてみましょうstd::listend反復子は、リスト内の「最後の要素の 1 つ後ろ」を指す必要があります。動的に割り当てられたノードが指すstd::list場所の実装が存在します。endデフォルトのコンストラクターでさえ、そのようなノードを割り当てて、 を呼び出すときにend()何かを指すようにします。

このような実装では、すべてend()のコンストラクターが指すノードを割り当てる必要があります。移動コンストラクターも同様です。その割り当ては失敗し、例外がスローされる可能性があります。

この同じ動作は、任意のノードベースのコンテナーに拡張できます。

「短い文字列」の最適化を行うこれらのノードベースのコンテナーの実装もあります。これらは、動的に割り当てるのではなく、コンテナー クラス自体にエンド ノードを埋め込みます。したがって、デフォルト コンストラクター (およびムーブ コンストラクター) は何も割り当てる必要はありません。

移動代入演算子はcontainer<X>、コンテナーのアロケーターpropagate_on_container_move_assignment::valueが false で、lhs のアロケーターが rhs のアロケーターと等しくない場合にスローできます。その場合、move 代入演算子はメモリの所有権を rhs から lhs に移すことを禁じられています。std::allocatorを使用している場合、 のすべてのインスタンスstd::allocatorが互いに等しいため、これは発生しません。

アップデート

propagate_on_container_move_assignment::valueが falseの場合の準拠した移植可能な例を次に示します。VS、gcc、およびclangの最新バージョンに対してテストされています。

#include <cassert>
#include <cstddef>
#include <iostream>
#include <vector>

template <class T>
class allocator
{
    int id_;
public:
    using value_type    = T;

    allocator(int id) noexcept : id_(id) {}
    template <class U> allocator(allocator<U> const& u) noexcept : id_(u.id_) {}

    value_type*
    allocate(std::size_t n)
    {
        return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
    }

    void
    deallocate(value_type* p, std::size_t) noexcept
    {
        ::operator delete(p);
    }

    template <class U, class V>
    friend
    bool
    operator==(allocator<U> const& x, allocator<V> const& y) noexcept
    {
        return x.id_ == y.id_;
    }
};

template <class T, class U>
bool
operator!=(allocator<T> const& x, allocator<U> const& y) noexcept
{
    return !(x == y);
}

template <class T> using vector = std::vector<T, allocator<T>>;

struct A
{
    static bool time_to_throw;

    A() = default;
    A(const A&) {if (time_to_throw) throw 1;}
    A& operator=(const A&) {if (time_to_throw) throw 1; return *this;}
};

bool A::time_to_throw = false;

int
main()
{
    vector<A> v1(5, A{}, allocator<A>{1});
    vector<A> v2(allocator<A>{2});
    v2 = std::move(v1);
    try
    {
        A::time_to_throw = true;
        v1 = std::move(v2);
        assert(false);
    }
    catch (int i)
    {
        std::cout << i << '\n';
    }
}

このプログラムは次を出力します。

1

これは、が false で、問題の 2 つのアロケーターが等しくないvector<T, A>場合に、移動代入演算子がその要素をコピー/移動していることを示します。propagate_on_container_move_assignment::valueこれらのコピー/ムーブのいずれかがスローされると、コンテナーのムーブ割り当てがスローされます。

于 2013-11-07T18:38:23.253 に答える
6

はい、投げ技のコンストラクターは実際に存在します。noexcept-movable がstd::pair<T, U>どこにあり、コピーのみ可能かを検討してください(コピーがスローできると仮定します)。次に、スローされる可能性のある便利な移動コンストラクターがあります。TUstd::pair<T, U>

std::move_if_noexcept必要に応じて、標準ライブラリにユーティリティがあります (std::vector::resize少なくとも基本的な例外保証を実装すると便利です)。

Move コンストラクターと強力な例外保証も参照してください。

于 2013-11-07T18:42:43.933 に答える