9

次の構造は、移動代入演算子を として宣言したため、C++11 でコンパイルできませんnoexcept

struct foo
{
  std::vector<int> data;
  foo& operator=(foo&&) noexcept = default;
};

コンパイラによって生成されるデフォルトのムーブ代入演算子は、noexcept(false)ムーブstd::vector<int>代入も同様であるためnoexcept(false)です。これは、デフォルトのアロケータが にstd::allocator_traits<T>:: propagate_on_container_move_assignment設定されているためstd::false_typeです。この質問も参照してください。

これは C++14 で修正されたと思います (ライブラリの欠陥 2103を参照)。

私の質問は、noexcept自分で定義せずにデフォルトの移動代入代入演算子を強制する方法はありますか?

これが不可能な場合、構造体に渡されるようstd::vector<int>noexcept、移動割り当て可能にする方法はありますか?noexcept(true)

4

2 に答える 2

7

これは C++14 で修正されていると思います (ライブラリの欠陥 2103 を参照)。

DR として、その修正は C++11 の修正と見なされる必要があるため、一部の C++11 実装では既に修正されています。

私の質問は、noexcept自分で定義せずにデフォルトの移動代入代入演算子を強制する方法はありますか?

デフォルトの移動代入演算子を使用noexceptするには、そのサブオブジェクトに移動代入演算子を持たせる必要がありますnoexcept

私が考えることができる最も明白なポータブルな方法はstd::vector、移動を強制するラッパーを使用することですnoexcept

template<typename T, typename A = std::allocator<T>>
  struct Vector : std::vector<T, A>
  {
    using vector::vector;

    Vector& operator=(Vector&& v) noexcept
    {
      static_cast<std::vector<T,A>&>(*this) = std::move(v);
      return *this;
    }
    Vector& operator=(const Vector&) = default;
  };

もう 1 つの同様のオプションは、DR 2013 修正プログラムを使用して独自のアロケーター タイプを定義し、それを使用することです。

template<typename T>
  struct Allocator : std::allocator<T>
  {
    Allocator() = default;
    template<typename U> Allocator(const Allocator<U>&) { }
    using propagate_on_container_move_assignment  = true_type;
    template<typename U> struct rebind { using other = Allocator<U>; };
  };

template<typename T>
  using Vector = std::vector<T, Allocator<T>>;

もう 1 つのオプションは、DR 2013 への解決を実装する GCC などの標準ライブラリ実装を使用することです。また、すべてのアロケーター インスタンスが等しいことがわかっている場合は、他のアロケーター タイプに対してstd::vectorの移動代入演算子を作成します。noexcept

于 2013-12-02T14:36:58.903 に答える
1

何かを強制できるとは思いませんが、ラップすることはできます:

#include <iostream>
#include <vector>

template <typename T>
struct Wrap
{
    public:
    Wrap() noexcept
    {
        new (m_value) T;
    }

    Wrap(const Wrap& other) noexcept
    {
        new (m_value) T(std::move(other.value()));
    }

    Wrap(Wrap&& other) noexcept
    {
        std::swap(value(), other.value());
    }


    Wrap(const T& other) noexcept
    {
        new (m_value) T(std::move(other));
    }

    Wrap(T&& other) noexcept
    {
        new (m_value) T(std::move(other));
    }

    ~Wrap() noexcept
    {
        value().~T();
    }

    Wrap& operator = (const Wrap& other) noexcept
    {
        value() = other.value();
        return *this;
    }

    Wrap& operator = (Wrap&& other) noexcept
    {
        value() = std::move(other.value());
        return *this;
    }

    Wrap& operator = (const T& other) noexcept
    {
        value() = other;
        return *this;
    }

    Wrap& operator = (T&& other) noexcept
    {
        value() = std::move(other);
        return *this;
    }

    T& value() noexcept { return *reinterpret_cast<T*>(m_value); }
    const T& value() const noexcept { return *reinterpret_cast<const T*>(m_value); }
    operator T& () noexcept { return value(); }
    operator const T& () const noexcept { return value(); }

    private:
    typename std::aligned_storage <sizeof(T), std::alignment_of<T>::value>::type m_value[1];
};


struct Foo
{
    public:
    Foo& operator = (Foo&&)  noexcept = default;

    std::vector<int>& data() noexcept { return m_data; }
    const std::vector<int>& data() const noexcept { return m_data; }

    private:
    Wrap<std::vector<int>> m_data;
};

int main() {
    Foo foo;
    foo.data().push_back(1);
    Foo boo;
    boo = std::move(foo);
    // 01
    std::cout << foo.data().size() << boo.data().size() << std::endl;
    return 0;
}

(ジョナサン・ウェイクリーに感謝)

于 2013-12-02T15:25:00.120 に答える