Reputation: 603
Is it valid to place custom preconditions on the state of the move target in a move assignment operator? In particular, would it be valid to only allow moving to an object that has not been fully initialized before?
Consider:
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;
}
};
I wonder, e.g., if this class can be used with a container, without hitting the assertion. E.g. if you do:
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());
Were this guaranteed to work with the class, or is this relying on implementation details of std::vector
?
Upvotes: 2
Views: 113
Reputation: 218780
Is it valid to place custom preconditions on the state of the move target in a move assignment operator?
Yes, but...
In particular, would it be valid to only allow moving to an object that has not been fully initialized before?
Sure, but...
Consider:
No problem so far, but...
std::vector<Foo> foo;
// ...
You can do anything you want with your class until you use it with std-code (or somebody else's code) which puts requirements on your type.
For example the standard says this about vector::erase
:
For
vector
anddeque
,T
is Cpp17MoveAssignable.
And Cpp17MoveAssignable is defined here.
t = rv
rv
’s state is unspecified. [Note:rv
must still meet the requirements of the library component that is using it, whether or nott
andrv
refer to the same object. The operations listed in those requirements must work as specified whetherrv
has been moved from or not. —end note]
Foo
doesn't fully meet the Cpp17MoveAssignable requirements. And that's fine as long as you don't expect Foo
to work with code that requires Cpp17MoveAssignable.
Disclaimer: A future standard might relax the requirements on vector::erase
so as to allow Foo
. But that is not the case today.
Note that std::remove_if
also requires Cpp17MoveAssignable:
http://eel.is/c++draft/alg.remove#2
And this slight modification of your program actually will assert:
#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;});
}
Upvotes: 1