Могу ли я заставить специальную функцию-член по умолчанию быть noexcept?

Следующая структура не компилируется в 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) передается в мою структуру?

9

Решение

Я считаю, что это было исправлено в 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 для других типов распределителя, когда известно, что все экземпляры распределителя сравниваются одинаково.

5

Другие решения

Я не думаю, что вы можете заставить что-либо, но вы можете обернуть это:

#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;
}

(Спасибо Джонатану Уэйкли)

1

По вопросам рекламы [email protected]