Действительны ли пользовательские предварительные условия для оператора назначения перемещения? - PullRequest
2 голосов
/ 20 февраля 2020

Допустимо ли размещать пользовательские предварительные условия для состояния цели перемещения в операторе назначения перемещения? В частности, допустимо ли разрешить перемещение только к объекту, который не был полностью инициализирован ранее?

Рассмотрим:

struct Foo {
private:
  std::unique_ptr<int> value;

public:
  Foo(std::unique_ptr<int> value) : value{std::move(value)} {}

  Foo(Foo&&) noexcept = default;
  Foo &operator =(Foo&& other) noexcept {
    assert(!value);
    value = std::move(other.value);
    return *this;
  }
};

Интересно, например, можно ли использовать этот класс с контейнером, не ударяя утверждение. Например, если вы делаете:

    std::vector<Foo> foo;
    foo.emplace_back(std::make_unique<int>(42));
    foo.emplace_back(std::make_unique<int>(17));
    Foo removed = std::move(foo[0]);
    foo.erase(foo.begin());

Было ли гарантировано, что это работает с классом, или это зависит от деталей реализации std::vector?

1 Ответ

1 голос
/ 20 февраля 2020

Допустимо ли помещать пользовательские предварительные условия в состояние цели перемещения в операторе назначения перемещения?

Да, но ...

В частности, было бы допустимо разрешить перемещение только к объекту, который не был полностью инициализирован ранее?

Конечно, но ...

Учтите:

Пока проблем нет, но ...

std::vector<Foo> foo;
// ...

Вы можете делать со своим классом все, что захотите, пока не будете использовать его со стандартным кодом (или чьим-либо другим кодом), который помещает Требования к вашему типу.

Например, стандарт гласит это о vector::erase:

Для vector и deque, T - это Cpp17MoveAssignable .

И Cpp17MoveAssignable определяется здесь .

t = rv

rv состояние не указано. [ Примечание : rv должен по-прежнему соответствовать требованиям библиотечного компонента, который его использует, независимо от того, относятся ли t и rv к одному и тому же объекту. Операции, перечисленные в этих требованиях, должны работать так, как указано, независимо от того, был ли перемещен rv или нет. - конец примечания ]

Foo не полностью соответствует требованиям Cpp17MoveAssignable. И это нормально, если вы не ожидаете, что Foo будет работать с кодом, для которого требуется Cpp17MoveAssignable.

Отказ от ответственности: Будущий стандарт может ослабить требования к vector::erase, чтобы разрешить Foo. Но сегодня это не так.

Обратите внимание, что std::remove_if также требует Cpp17MoveAssignable :

http://eel.is/c++draft/alg.remove#2

И эта небольшая модификация вашей программы на самом деле будет утверждать:

#include <algorithm>
#include <cassert>
#include <memory>
#include <vector>

struct Foo {
private:
  std::unique_ptr<int> value;

public:
  Foo(std::unique_ptr<int> value) : value{std::move(value)} {}

  Foo(Foo&&) noexcept = default;
  Foo &operator =(Foo&& other) noexcept {
    assert(!value);
    value = std::move(other.value);
    return *this;
  }

  bool operator==(int i) const {return *value == i;}
};

int
main()
{
    std::vector<Foo> foo;
    foo.push_back(std::make_unique<int>(1));
    foo.push_back(std::make_unique<int>(2));
    foo.push_back(std::make_unique<int>(3));
    std::remove_if(foo.begin(), foo.end(),
        [](auto const& f) {return f == 1;});
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...