Следующая структура не компилируется в 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)
передается в мою структуру?
Я считаю, что это было исправлено в 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;
};
Другой подобный вариант — определить собственный тип распределителя с помощью исправления 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>>;
Другой вариант заключается в использовании стандартной реализации библиотеки, такой как GCC, которая реализует разрешение для DR 2013, а также делает std::vector
оператор присваивания ходов noexcept
для других типов распределителя, когда известно, что все экземпляры распределителя сравниваются одинаково.
Я не думаю, что вы можете заставить что-либо, но вы можете обернуть это:
#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;
}
(Спасибо Джонатану Уэйкли)